1. CMake 基础概念
1.1 CMake 工作流程
CMake 构建过程分为两个主要阶段:
1
| cmake .. -DBUILD_32BIT=ON -DTARGET_TYPE=exe
|
配置阶段的作用:
构建阶段 (Build)
构建阶段的作用:
执行实际的编译过程
调用编译器和链接器
生成最终的可执行文件或库
1.2 project() 命令详解
project() 的执行内容
project() 执行的主要任务:
编译器检测和验证
语言支持初始化
工具链配置
平台特定设置
内置变量设置
缓存变量创建
编译器检测过程
1 2 3 4 5 6 7 8 9
| # CMake 会创建临时测试文件 # /build/CMakeFiles/CMakeScratch/TryCompile-xxxxx/testCCompiler.c int main(void) { return 0; }
# 然后执行编译测试 ${CMAKE_C_COMPILER} -o testCCompiler testCCompiler.c
# 如果成功,设置 CMAKE_C_COMPILER_WORKS = 1 # 如果失败,报错并停止配置
|
为什么编译器设置要在 project() 之前
1 2 3 4 5 6 7
| project(packer) set(CMAKE_C_COMPILER /path/to/clang)
set(CMAKE_C_COMPILER /path/to/clang) project(packer)
|
跳过编译器测试
1 2 3
| set(CMAKE_C_COMPILER_WORKS 1) set(CMAKE_CXX_COMPILER_WORKS 1) project(packer)
|
适用场景:
交叉编译环境
编译器测试失败但编译器本身正常
自定义工具链
避免链接器配置问题
1.3 编译和链接过程
编译链接流程图
1
| 源代码(.cpp) → [编译器] → 目标文件(.o/.obj) → [链接器] → 可执行文件(.exe)
|
编译阶段 (Compilation):
1.4 CMake 版本管理
版本要求
1
| cmake_minimum_required(VERSION 3.15)
|
版本说明:
2. CMake 变量与配置
2.1 内置变量
常用内置变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| CMAKE_SOURCE_DIR CMAKE_BINARY_DIR CMAKE_CURRENT_SOURCE_DIR CMAKE_CURRENT_BINARY_DIR •
CMAKE_BUILD_TYPE CMAKE_CXX_COMPILER CMAKE_C_COMPILER CMAKE_LINKER •
CMAKE_SYSTEM_NAME CMAKE_SYSTEM_PROCESSOR CMAKE_HOST_SYSTEM_NAME
|
CMAKE_CURRENT_* 变量详解
1 2 3 4 5 6 7
| CMAKE_CURRENT_SOURCE_DIR CMAKE_CURRENT_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR CMAKE_CURRENT_BINARY_DIR
|
重要说明:
编译器相关变量
1 2 3 4
| CMAKE_CXX_COMPILER_ID CMAKE_CXX_COMPILER_VERSION CMAKE_CXX_STANDARD CMAKE_CXX_STANDARD_REQUIRED
|
2.2 变量作用域和传递机制
变量作用域类型
全局变量(Global Variables):
1 2 3 4 5 6
| set(CMAKE_CXX_STANDARD 17) set(CMAKE_BUILD_TYPE "Debug") set(CMAKE_SYSTEM_NAME Windows)
add_subdirectory(libs/lzma)
|
自动传递的变量:
不会自动传递的变量:
自定义变量(但子项目可以访问)
目标特定的配置(target_* 命令)
变量传递的时机问题
1 2 3 4 5 6 7 8
| cmake_minimum_required(VERSION 3.15) project(packer) add_subdirectory(libs/lzma)
set(CMAKE_CXX_STANDARD 17) set(ARCH "x64") set(MINGW_TRIPLE "x86_64-w64-mingw32")
|
正确的顺序:
1 2 3 4 5 6 7 8 9 10
| cmake_minimum_required(VERSION 3.15) project(packer)
set(CMAKE_CXX_STANDARD 17) set(ARCH "x64") set(MINGW_TRIPLE "x86_64-w64-mingw32")
add_subdirectory(libs/lzma)
|
2.3 用户自定义变量
option 变量(布尔类型)
1 2 3 4 5 6 7 8 9 10
| option(BUILD_32BIT "Build 32-bit version" OFF) option(ENABLE_TESTS "Enable unit tests" ON) option(USE_STATIC_LIBS "Use static libraries" OFF) •
if(BUILD_32BIT) message(STATUS "Building 32-bit version") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") endif()
|
set 变量(通用类型)
1 2 3 4 5 6 7 8 9 10 11 12 13
| set(TARGET_TYPE "exe" CACHE STRING "Target type: exe or dll") set_property(CACHE TARGET_TYPE PROPERTY STRINGS exe dll) •
set(CUSTOM_INCLUDE_DIR "/usr/local/include" CACHE PATH "Custom include directory") •
set(SOURCE_FILES main.cpp utils.cpp config.cpp )
|
变量作用域
1 2 3 4 5 6
| set(LOCAL_VAR "value")
set(CACHE_VAR "value" CACHE STRING "Description")
set(ENV{PATH} "/new/path:$ENV{PATH}")
|
2.4 命令行变量传递
-D 参数使用
1 2 3 4 5 6 7 8 9 10 11
| cmake .. -DBUILD_32BIT=ON -DENABLE_TESTS=OFF
cmake .. -DTARGET_TYPE=dll -DCMAKE_BUILD_TYPE=Release
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCUSTOM_LIB_DIR=/opt/libs
cmake .. -DBUILD_32BIT=ON -DTARGET_TYPE=exe -DCMAKE_BUILD_TYPE=Debug
cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
重要说明:
2.5 生成器表达式
基本语法
1 2 3 4 5 6 7 8
| $<condition:true_value> $<condition:true_value:false_value>
target_compile_options(myapp PRIVATE $<$<CONFIG:Debug>:-g -O0> $<$<CONFIG:Release>:-O3 -DNDEBUG> )
|
实用生成器表达式
1 2 3 4 5 6 7 8 9 10 11 12
| $<$<STREQUAL:${SUBSYSTEM_TYPE},gui>:-Wl,--subsystem,windows> $<$<STREQUAL:${SUBSYSTEM_TYPE},cui>:-Wl,--subsystem,console>
$<$<BOOL:${BUILD_32BIT}>:-m32> $<$<NOT:$<BOOL:${USE_SHARED_LIBS}>>:-static>
$<$<CXX_COMPILER_ID:GNU>:-fPIC> $<$<CXX_COMPILER_ID:Clang>:-fcolor-diagnostics>
$<$<PLATFORM_ID:Windows>:-DWIN32> $<$<PLATFORM_ID:Linux>:-DLINUX>
|
等价的 if 语句写法
1 2 3 4 5 6 7 8
| target_link_options(${TARGET_NAME} PRIVATE $<$<STREQUAL:${SUBSYSTEM_TYPE},gui>:-Wl,--subsystem,windows> )
if(SUBSYSTEM_TYPE STREQUAL "gui") target_link_options(${TARGET_NAME} PRIVATE -Wl,--subsystem,windows) endif()
|
3. CMake 中的 PRIVATE/PUBLIC 可见性详解
3.1 概述
CMake 中的 PRIVATE、PUBLIC 和 INTERFACE 关键字控制的是依赖传递性,而不是简单的可见性或 GUI 显示。它们决定了在项目依赖关系中,哪些配置会自动传递给依赖者。
3.2 三种可见性类型
3.2.1 PRIVATE
作用范围:只对当前目标可见
传递性:不会传递给依赖当前目标的其他目标
使用场景:内部实现细节、私有头文件、内部依赖
3.2.2 PUBLIC
作用范围:对当前目标和依赖者都可见
传递性:会自动传递给依赖当前目标的其他目标
使用场景:公共接口、用户需要的头文件
3.2.3 INTERFACE
作用范围:只对依赖者可见,当前目标不使用
传递性:只传递给依赖者
使用场景:header-only 库、纯接口定义
3.3 实际应用示例
3.3.1 库的配置示例
1 2 3 4 5 6 7 8 9 10 11 12
| add_library(lzma STATIC ${LZMA_SOURCES})
target_include_directories(lzma PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/wrapper )
target_include_directories(lzma PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/easylzma ${CMAKE_CURRENT_SOURCE_DIR}/include/pavlov )
|
3.3.2 使用库的项目配置
1 2 3 4 5 6 7 8 9 10
| add_subdirectory(libs/lzma) add_executable(packer ${PACKER_SOURCES})
target_include_directories(packer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include )
target_link_libraries(packer PRIVATE lzma)
|
3.4 编译命令的实际差异
3.4.1 使用 PRIVATE 时
1 2 3 4
| clang++ -I./include -I./include/wrapper -c src/file.cpp
clang++ -I./include -I./libs/lzma/include/wrapper -c src/main.cpp
|
3.4.2 使用 PUBLIC 时
1 2 3 4 5
| clang++ -I./include -I./include/wrapper -c src/file.cpp
clang++ -I./include -I./libs/lzma/include/wrapper -c src/main.cpp
|
3.5 重要限制:仅在项目依赖时有效
3.5.1 有效场景:项目内依赖
1 2 3
| add_subdirectory(libs/lzma) target_link_libraries(packer PRIVATE lzma)
|
PRIVATE/PUBLIC 有效
PUBLIC 的配置会自动传递
实现自动依赖管理
3.5.2 无效场景:独立编译
1 2 3 4
|
target_link_libraries(packer PRIVATE /path/to/liblzma.a) target_include_directories(packer PRIVATE /path/to/lzma/include/)
|
PRIVATE/PUBLIC 无效
必须手动管理所有头文件路径
CMake 不知道 .a 文件对应的头文件位置
3.6 判断原则
3.6.1 使用 PRIVATE 当:
头文件是内部实现细节
用户不需要直接包含这些头文件
第三方库的内部依赖
3.6.2 使用 PUBLIC 当:
头文件是库的公共接口
用户使用库时需要包含这些头文件
库的公共 API 定义
3.6.3 使用 INTERFACE 当:
创建 header-only 库
纯接口定义
库本身不需要,但使用者需要
3.7 最佳实践
明确区分公共接口和内部实现
公共接口使用 PUBLIC,内部实现使用 PRIVATE
在项目依赖场景下,充分利用自动传递特性
独立编译场景下,手动管理依赖关系
使用 VERBOSE=1 验证实际的编译命令
3.8 验证方法
1 2 3 4
| make VERBOSE=1
cmake --build . --verbose
|
通过观察编译命令中的 -I 参数,可以验证头文件路径的传递是否符合预期。
4. 项目结构与目标管理
4.1 add_subdirectory() 的添加时机
在 CMake 配置阶段执行
1 2 3 4 5 6 7 8 9
| cmake_minimum_required(VERSION 3.15) project(packer)
add_subdirectory(libs/lzma) add_subdirectory(libs/common)
add_executable(packer src/main.cpp)
|
位置要求
1 2 3 4 5 6
| add_subdirectory(libs/lzma) add_subdirectory(libs/common)
add_executable(packer src/main.cpp) target_link_libraries(packer PRIVATE lzma common)
|
1 2 3 4 5 6
| add_executable(packer src/main.cpp) target_link_libraries(packer PRIVATE lzma common)
add_subdirectory(libs/lzma) add_subdirectory(libs/common)
|
4.2 add_executable() 的添加时机
基本作用
1
| add_executable(packer src/main.cpp src/compressor.cpp)
|
add_executable() 的作用:
创建一个可执行文件目标
注册目标到 CMake 的内部目标系统
为后续的 target_* 命令做准备
不会立即编译,只是定义目标
位置要求
1 2 3 4 5 6 7 8
| add_executable(packer src/main.cpp) target_link_libraries(packer PRIVATE lzma) target_compile_options(packer PRIVATE -Wall)
target_link_libraries(packer PRIVATE lzma) add_executable(packer src/main.cpp)
|
4.3 推荐的项目结构
标准的组织顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| cmake_minimum_required(VERSION 3.15)
set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_CXX_COMPILER /path/to/clang++)
project(packer)
set(CMAKE_CXX_STANDARD 17) set(ARCH "x64") set(MINGW_TRIPLE "x86_64-w64-mingw32")
add_subdirectory(libs/common) add_subdirectory(libs/lzma)
set(PACKER_SOURCES src/main.cpp src/compressor.cpp )
add_executable(packer ${PACKER_SOURCES})
target_include_directories(packer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(packer PRIVATE lzma common kernel32 user32) target_compile_options(packer PRIVATE -Wall -Wextra)
if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(packer PRIVATE _DEBUG) endif()
set_target_properties(packer PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" )
|
4.4 引用子项目的正确操作
推荐的目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| project_root/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ └── compressor.cpp ├── include/ │ └── compressor.h ├── libs/ │ ├── lzma/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── wrapper/ │ │ │ │ └── easylzmawrapper.h │ │ │ ├── easylzma/ │ │ │ └── pavlov/ │ │ └── src/ │ │ ├── wrapper/ │ │ ├── pavlov/ │ │ └── ... │ └── common/ │ ├── CMakeLists.txt │ ├── include/ │ └── src/ └── build/
|
子项目 CMakeLists.txt 编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| cmake_minimum_required(VERSION 3.15) project(lzma)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(LZMA_SOURCES src/wrapper/easylzmawrapper.cpp src/compress.c src/decompress.c )
add_library(lzma STATIC ${LZMA_SOURCES})
target_include_directories(lzma PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/wrapper )
target_include_directories(lzma PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/easylzma ${CMAKE_CURRENT_SOURCE_DIR}/include/pavlov )
target_compile_options(lzma PRIVATE -Wall -Wextra )
target_compile_options(lzma PRIVATE $<$<CONFIG:Debug>:-O0 -g> $<$<CONFIG:Release>:-O2> )
|
主项目 CMakeLists.txt 编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| cmake_minimum_required(VERSION 3.15)
set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_CXX_COMPILER /root/OLLVM_17/build/bin/clang++) set(CMAKE_C_COMPILER /root/OLLVM_17/build/bin/clang)
project(packer)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_SKIP_RPATH TRUE)
set(ARCH "x64") set(MINGW_TRIPLE "x86_64-w64-mingw32")
if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif()
add_subdirectory(libs/lzma)
set(PACKER_SOURCES src/main.cpp src/compressor.cpp )
add_executable(packer ${PACKER_SOURCES})
target_include_directories(packer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include )
target_link_libraries(packer PRIVATE lzma kernel32 user32 )
target_compile_options(packer PRIVATE -m64 --target=${MINGW_TRIPLE} -Wall -Wextra )
target_link_options(packer PRIVATE -m64 --target=${MINGW_TRIPLE} )
|
5. 交叉编译环境搭建
5.1 MinGW-w64 工具链安装
Ubuntu 系统安装
1 2 3 4 5 6 7 8
| sudo apt update
sudo apt install gcc-mingw-w64-i686 g++-mingw-w64-i686
sudo apt install gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64
sudo apt install mingw-w64 mingw-w64-tools gcc-mingw-w64 g++-mingw-w64
|
CentOS/RHEL 系统安装
1 2 3 4 5
| sudo yum install epel-release
sudo yum install mingw32-gcc mingw32-gcc-c++ sudo yum install mingw64-gcc mingw64-gcc-c++
|
5.2 工具链验证
验证安装
1 2 3 4 5 6 7 8 9 10
| i686-w64-mingw32-gcc --version i686-w64-mingw32-g++ --version i686-w64-mingw32-ar --version i686-w64-mingw32-windres --version
x86_64-w64-mingw32-gcc --version x86_64-w64-mingw32-g++ --version x86_64-w64-mingw32-ar --version x86_64-w64-mingw32-windres --version
|
查找工具链位置
1 2 3 4 5 6
| which i686-w64-mingw32-gcc which x86_64-w64-mingw32-gcc
find /usr -name "*mingw*" -type d 2>/dev/null ls -la /usr/bin/*mingw*
|
5.3 简单编译测试
创建测试程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // hello.cpp
using namespace std; int main() { cout << "Hello from MinGW cross-compilation!" << endl;
cout << "Target: Windows" << endl;
cout << "Architecture: 64-bit" << endl;
cout << "Architecture: 32-bit" << endl;
return 0; }
|
编译测试
1 2 3 4 5 6
| i686-w64-mingw32-g++ -o hello32.exe hello.cpp
x86_64-w64-mingw32-g++ -o hello64.exe hello.cpp
x86_64-w64-mingw32-g++ -static -o hello_static.exe hello.cpp
|
5.4 目标三元组理解
三元组格式
常见三元组
1 2 3 4
| x86_64-w64-mingw32 i686-w64-mingw32 x86_64-linux-gnu arm-linux-gnueabihf
|
重要说明:
6. 交叉编译配置
6.1 CMake 工具链文件
32位 Windows 工具链文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR i686)
set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) set(CMAKE_AR i686-w64-mingw32-ar) set(CMAKE_RANLIB i686-w64-mingw32-ranlib)
set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
64位 Windows 工具链文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) set(CMAKE_AR x86_64-w64-mingw32-ar) set(CMAKE_RANLIB x86_64-w64-mingw32-ranlib)
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
6.2 使用工具链文件
配置命令
1 2 3 4 5 6
| cmake -DCMAKE_TOOLCHAIN_FILE=mingw32.cmake ..
cmake -DCMAKE_TOOLCHAIN_FILE=mingw64.cmake ..
cmake -DCMAKE_TOOLCHAIN_FILE=mingw64.cmake -DCMAKE_BUILD_TYPE=Release ..
|
6.3 直接设置编译器
简单设置方式
1 2 3 4
| set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
命令行设置
1 2 3 4 5
| cmake .. \ -DCMAKE_SYSTEM_NAME=Windows \ -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \ -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++
|
6.4 架构特定配置
条件编译配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| if(ARCH STREQUAL "x86") set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) target_include_directories(${TARGET_NAME} PRIVATE /usr/i686-w64-mingw32/include ) target_compile_options(${TARGET_NAME} PRIVATE --target=i686-pc-windows-gnu --sysroot=/usr/i686-w64-mingw32 -m32 ) else() set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) target_include_directories(${TARGET_NAME} PRIVATE /usr/x86_64-w64-mingw32/include ) target_compile_options(${TARGET_NAME} PRIVATE --target=x86_64-pc-windows-gnu --sysroot=/usr/x86_64-w64-mingw32 -m64 ) endif()
|
6.5 自动宏定义
编译器自动定义的宏
1 2 3 4 5 6 7 8 9
| // 32位编译时自动定义 WIN32 // Windows平台 **i386** // x86架构 _WIN32 // Win32 API // 64位编译时自动定义 WIN32 // Windows平台(仍然定义) _WIN64 // 64位Windows __x86_64__ // x86_64架构 _WIN32 // Win32 API(仍然定义)
|
在代码中使用
1 2 3 4 5 6 7 8 9 10
| // Windows特定代码 // 64位Windows特定代码 cout << "Windows 64-bit" << endl; // 32位Windows特定代码 cout << "Windows 32-bit" << endl;
|
7. 编译器与链接器选项
7.1 编译器选项分类
警告控制选项 (-W系列)
1 2 3 4 5 6 7 8
| target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wno-unknown-pragmas -Wno-unknown-attributes -Wno-microsoft-annotations -Wno-unused-parameter )
|
功能控制选项 (-f系列)
1 2 3 4 5 6 7 8 9
| target_compile_options(${TARGET_NAME} PRIVATE -fno-exceptions -fno-rtti -fno-stack-protector -ffunction-sections -fdata-sections -fno-lto -fPIC )
|
优化选项 (-O系列)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(${TARGET_NAME} PRIVATE -O0 -g -DDEBUG ) endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_options(${TARGET_NAME} PRIVATE -O3 -DNDEBUG -g0 -fomit-frame-pointer ) endif()
if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") target_compile_options(${TARGET_NAME} PRIVATE -Os -DNDEBUG ) endif()
|
架构相关选项
1 2 3 4 5 6 7
| target_compile_options(${TARGET_NAME} PRIVATE -m32)
target_compile_options(${TARGET_NAME} PRIVATE -m64)
target_compile_options(${TARGET_NAME} PRIVATE -march=native) target_compile_options(${TARGET_NAME} PRIVATE -mtune=generic)
|
7.2 链接器选项
基本链接选项
1 2 3 4 5
| target_link_options(${TARGET_NAME} PRIVATE -static -static-libgcc -static-libstdc++ )
|
Windows 特定链接选项
1 2 3 4 5 6
| target_link_options(${TARGET_NAME} PRIVATE -Wl,--subsystem,console -Wl,--subsystem,windows -Wl,--entry,main -Wl,--out-implib,libname.a )
|
优化链接选项
1 2 3 4 5 6 7
| target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections -Wl,--strip-all -Wl,--strip-debug -Wl,--allow-multiple-definition -s )
|
7.3 -Wl 参数详解
-Wl 的含义
-Wl = “传递给链接器”的前缀
逗号分隔多个链接器参数
编译器会将 -Wl, 后的参数直接传递给链接器
示例用法
1 2 3 4 5 6 7 8 9 10 11 12
| target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections)
target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections,--strip-all,--subsystem,console )
target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections -Wl,--strip-all -Wl,--subsystem,console )
|
7.4 链接器选择
lld-link vs ld.lld
在Linux下交叉编译生成Windows可执行文件时:
lld-link: 生成PE/COFF格式(Windows可执行文件) ld.lld: 生成ELF格式(Linux可执行文件)
1 2 3 4 5 6 7 8 9 10 11
| set(CMAKE_LINKER lld-link) set(CMAKE_C_LINK_EXECUTABLE "lld-link <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> /OUT:<TARGET> <LINK_LIBRARIES>" ) set(CMAKE_CXX_LINK_EXECUTABLE "lld-link <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> /OUT:<TARGET> <LINK_LIBRARIES>" )
set(CMAKE_C_COMPILER_TARGET x86_64-pc-windows-msvc) set(CMAKE_CXX_COMPILER_TARGET x86_64-pc-windows-msvc)
|
7.5 库和启动文件控制
控制标准库链接
1 2 3 4 5 6 7 8 9 10 11 12
| target_compile_options(${TARGET_NAME} PRIVATE -nostdlib -nostartfiles -nodefaultlibs )
target_link_libraries(${TARGET_NAME} PRIVATE gcc mingw32 kernel32 user32 )
|
8. 实际项目配置
8.1 完整的 CMakeLists.txt 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| cmake_minimum_required(VERSION 3.15) project(CrossCompileExample VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(BUILD_32BIT "Build 32-bit version" OFF) set(TARGET_TYPE "exe" CACHE STRING "Target type: exe or dll") set_property(CACHE TARGET_TYPE PROPERTY STRINGS exe dll) set(SUBSYSTEM_TYPE "cui" CACHE STRING "Subsystem type: cui or gui") set_property(CACHE SUBSYSTEM_TYPE PROPERTY STRINGS cui gui)
if(BUILD_32BIT) set(ARCH "x86") message(STATUS "Building for 32-bit architecture") else() set(ARCH "x86_64") message(STATUS "Building for 64-bit architecture") endif()
if(TARGET_TYPE STREQUAL "dll") set(TARGET_NAME "${PROJECT_NAME}_${ARCH}") else() set(TARGET_NAME "${PROJECT_NAME}_${ARCH}") endif()
set(SOURCES src/main.cpp src/utils.cpp src/config.cpp ) set(HEADERS include/utils.h include/config.h )
if(TARGET_TYPE STREQUAL "dll") add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) else() add_executable(${TARGET_NAME} ${SOURCES} ${HEADERS}) endif()
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include )
if(CMAKE_SYSTEM_NAME STREQUAL "Windows") if(ARCH STREQUAL "x86") target_include_directories(${TARGET_NAME} PRIVATE /usr/i686-w64-mingw32/include ) target_compile_options(${TARGET_NAME} PRIVATE -m32 --sysroot=/usr/i686-w64-mingw32 ) else() target_include_directories(${TARGET_NAME} PRIVATE /usr/x86_64-w64-mingw32/include ) target_compile_options(${TARGET_NAME} PRIVATE -m64 --sysroot=/usr/x86_64-w64-mingw32 ) endif() endif()
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wno-unknown-pragmas -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections )
target_compile_options(${TARGET_NAME} PRIVATE $<$<CONFIG:Debug>:-O0 -g -DDEBUG> $<$<CONFIG:Release>:-O3 -DNDEBUG -g0 -fomit-frame-pointer> $<$<CONFIG:MinSizeRel>:-Os -DNDEBUG> )
target_link_options(${TARGET_NAME} PRIVATE -static -static-libgcc -static-libstdc++ -Wl,--gc-sections )
if(CMAKE_SYSTEM_NAME STREQUAL "Windows") target_link_options(${TARGET_NAME} PRIVATE $<$<STREQUAL:${SUBSYSTEM_TYPE},gui>:-Wl,--subsystem,windows> $<$<STREQUAL:${SUBSYSTEM_TYPE},cui>:-Wl,--subsystem,console> ) endif()
target_link_options(${TARGET_NAME} PRIVATE $<$<CONFIG:Release>:-s -Wl,--strip-all> )
if(WIN32) target_link_libraries(${TARGET_NAME} PRIVATE kernel32 user32 gdi32 winspool comdlg32 advapi32 shell32 ole32 oleaut32 uuid odbc32 odbccp32 ) endif()
set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
install(TARGETS ${TARGET_NAME} RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib )
message(STATUS "Project: ${PROJECT_NAME}") message(STATUS "Target: ${TARGET_NAME}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Target type: ${TARGET_TYPE}") message(STATUS "Architecture: ${ARCH}") message(STATUS "Subsystem: ${SUBSYSTEM_TYPE}") message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER}") message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
|
8.2 构建脚本示例
build.sh 脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
set -e
BUILD_TYPE="Release" ARCH="x86_64" TARGET_TYPE="exe" SUBSYSTEM="cui" CLEAN=false while [[ $ case $1 in --debug) BUILD_TYPE="Debug" shift ;; --32bit) ARCH="x86" shift ;; --dll) TARGET_TYPE="dll" shift ;; --gui) SUBSYSTEM="gui" shift ;; --clean) CLEAN=true shift ;; *) echo "Unknown option: $1" exit 1 ;; esac done
if [ "$CLEAN" = true ]; then echo "Cleaning build directory..." rm -rf build fi
mkdir -p build cd build
echo "Configuring CMake..." if [ "$ARCH" = "x86" ]; then cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw32.cmake \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DBUILD_32BIT=ON \ -DTARGET_TYPE=$TARGET_TYPE \ -DSUBSYSTEM_TYPE=$SUBSYSTEM else cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw64.cmake \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DBUILD_32BIT=OFF \ -DTARGET_TYPE=$TARGET_TYPE \ -DSUBSYSTEM_TYPE=$SUBSYSTEM fi
echo "Building..." make -j$(nproc) echo "Build completed successfully!" echo "Output directory: $(pwd)/bin" ls -la bin/
|
使用示例
1 2 3 4 5 6 7 8 9 10
| ./build.sh
./build.sh --32bit --debug
./build.sh --gui
./build.sh --dll
./build.sh --clean
|
9. 问题排查与最佳实践
9.1 常见问题及解决方案
1. 编译器找不到
错误信息:
1 2
| make[2]: /root/OLLVM_17/build/bin/clang++: Permission denied make[2]: /root/OLLVM_17/build/bin/clang++: No such file or directory
|
排查步骤:
1 2 3 4 5 6 7 8 9 10
| ls -la /root/OLLVM_17/build/bin/clang++
chmod +x /root/OLLVM_17/build/bin/clang++
readlink /root/OLLVM_17/build/bin/clang++
ldd /root/OLLVM_17/build/bin/clang-17
set(CMAKE_CXX_COMPILER clang++)
|
2. 路径不匹配错误
错误信息:
1
| The source directory "/home/user/old_path/..." does not exist.
|
解决方法:
1 2 3 4 5 6 7 8 9
| rm -rf CMakeCache.txt CMakeFiles/
cmake .. -DBUILD_32BIT=ON -DCMAKE_BUILD_TYPE=Release
rm -rf build/ mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw64.cmake make
|
3. 架构配置错误
问题: 32位编译使用了64位头文件路径
正确配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if(ARCH STREQUAL "x86") target_include_directories(${TARGET_NAME} PRIVATE /usr/i686-w64-mingw32/include ) set(MINGW_PREFIX "i686-w64-mingw32") else() target_include_directories(${TARGET_NAME} PRIVATE /usr/x86_64-w64-mingw32/include ) set(MINGW_PREFIX "x86_64-w64-mingw32") endif()
set(CMAKE_C_COMPILER ${MINGW_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${MINGW_PREFIX}-g++)
|
4. 链接错误
常见链接错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if(ARCH STREQUAL "x86") link_directories(/usr/i686-w64-mingw32/lib) else() link_directories(/usr/x86_64-w64-mingw32/lib) endif()
target_link_libraries(${TARGET_NAME} PRIVATE kernel32 user32 gdi32 winspool comdlg32 advapi32 shell32 ole32 oleaut32 uuid odbc32 odbccp32 )
target_link_options(${TARGET_NAME} PRIVATE -static-libgcc -static-libstdc++ )
|
9.2 调试技巧
CMake 调试
1 2 3 4 5 6 7 8 9
| message(STATUS "CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}") message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") message(STATUS "BUILD_32BIT: ${BUILD_32BIT}") message(STATUS "ARCH: ${ARCH}")
set(CMAKE_VERBOSE_MAKEFILE ON)
make VERBOSE=1
|
查看实际执行的命令
1 2 3 4 5 6 7
| make VERBOSE=1
export VERBOSE=1 make
make VERBOSE=1 2>&1 | grep -E "(gcc|g\+\+|ld)"
|
测试生成的可执行文件
1 2 3 4 5 6 7 8
| file output.exe
objdump -p output.exe | grep "DLL Name"
objdump -t output.exe
objdump -h output.exe
|
9.3 最佳实践
1. 项目结构建议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| project/ ├── CMakeLists.txt ├── cmake/ │ ├── mingw32.cmake │ ├── mingw64.cmake │ └── Modules/ ├── src/ │ ├── main.cpp │ └── ... ├── include/ │ └── ... ├── build/ └── scripts/ ├── build.sh └── clean.sh
|
2. CMakeLists.txt 组织
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| cmake_minimum_required(VERSION 3.15) project(ProjectName VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17) option(BUILD_32BIT "Build 32-bit version" OFF)
if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows") endif()
|
3. 版本控制
1 2 3 4 5 6 7 8 9 10
| build/ *.exe *.dll *.a *.so CMakeCache.txt CMakeFiles/ Makefile cmake_install.cmake
|
4. 持续集成配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| name: Cross Compile Build on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: arch: [x86, x86_64] build_type: [Debug, Release] steps: - uses: actions/checkout@v2 - name: Install MinGW run: | sudo apt update sudo apt install mingw-w64 - name: Configure run: | mkdir build && cd build if [ "${{ matrix.arch }}" = "x86" ]; then cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw32.cmake \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DBUILD_32BIT=ON else cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw64.cmake \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DBUILD_32BIT=OFF fi - name: Build run: | cd build make -j$(nproc) - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: build-${{ matrix.arch }}-${{ matrix.build_type }} path: build/bin/
|
5. 清理和重建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
echo "Cleaning build artifacts..."
rm -rf CMakeCache.txt rm -rf CMakeFiles/ rm -rf cmake_install.cmake rm -rf Makefile
rm -rf build/
find . -name "*.exe" -delete find . -name "*.dll" -delete find . -name "*.a" -delete echo "Clean completed!"
|
9.4 性能优化建议
1. 编译优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_options(${TARGET_NAME} PRIVATE -O3 -DNDEBUG -fomit-frame-pointer -ffast-math -funroll-loops -ffunction-sections -fdata-sections ) target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections -Wl,--strip-all ) endif()
|
2. 大小优化
1 2 3 4 5 6 7 8 9 10 11
| target_compile_options(${TARGET_NAME} PRIVATE -Os -fno-exceptions -fno-rtti -fno-stack-protector ) target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections -s )
|
9.5 容器化开发
Dockerfile 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| FROM ubuntu:20.04
RUN apt-get update && apt-get install -y \ build-essential \ cmake \ mingw-w64 \ git \ && rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
COPY . .
RUN mkdir build && cd build && \ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw64.cmake && \ make CMD ["bash"]
|
Docker 构建命令
1 2 3 4 5 6
| docker build -t cross-compile-env .
docker run -it --rm -v $(pwd):/workspace cross-compile-env
docker run --rm -v $(pwd):/workspace cross-compile-env make -C build
|
9.6 使用 VSCode + SSH + Docker 的开发环境搭建
9.6.1 开发环境架构概述
环境架构图:
1 2 3 4
| WSL 文件系统 ←→ Docker 容器 ←→ VSCode Remote-SSH ↑ ↑ ↑ 代码存储 编译环境 开发界面 (持久化) (工具链+库) (编辑+调试)
|
优势:
9.6.2 环境准备
前置条件检查:
1 2 3 4 5 6 7 8 9 10
| # 1. 检查 Docker 是否可用 docker --version docker ps
# 2. 检查项目目录结构 ls -la /path/to/your/project # 应该看到:CMakeLists.txt, src/, libs/, include/
# 3. 检查 Docker 镜像是否存在 docker images | grep your_image_name
|
如果 Docker 权限有问题:
1 2 3 4 5 6 7 8 9
| # 添加用户到 docker 组 sudo usermod -aG docker $USER
# 重启 WSL(在 Windows PowerShell 中执行) wsl --shutdown wsl
# 验证权限修复 docker ps # 应该不需要 sudo
|
9.6.3 创建开发容器
完整的容器创建命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| # 停止并删除旧容器(如果存在) sudo docker stop old_container_name 2>/dev/null sudo docker rm old_container_name 2>/dev/null
# 创建新的开发容器 sudo docker run -d \ --name packer-dev \ -p 2222:22 \ -v /home/trg/share_work:/home \ -w /home/kfx_project/compress_pack/packer \ ollvm_dev_rel \ bash -c " echo '=== 开始配置开发环境 ===' && \ apt update && \ apt install -y openssh-server vim nano tree htop && \ echo '=== 设置 root 密码为 dev123 ===' && \ echo 'root:dev123' | chpasswd && \ echo '=== 配置 SSH 服务 ===' && \ echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config && \ echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config && \ echo '=== 启动 SSH 服务 ===' && \ service ssh start && \ echo '=== 配置完成 ===' && \ echo 'SSH 连接信息:' && \ echo ' 地址: ssh root@localhost -p 2222' && \ echo ' 密码: dev123' && \ echo ' 项目目录: /home/kfx_project/compress_pack/packer' && \ tail -f /dev/null "
|
命令参数详解:
docker run -d:后台运行容器
--name packer-dev:给容器命名,方便后续管理
-p 2222:22:端口映射,将容器的22端口映射到主机的2222端口
-v /home/trg/share_work:/home:目录挂载,WSL目录映射到容器目录
-w /home/kfx_project/compress_pack/packer:设置工作目录为项目根目录
ollvm_dev_rel:使用的 Docker 镜像名称
bash -c "...":在容器中执行的初始化脚本
初始化脚本说明:
apt update && apt install -y openssh-server:安装 SSH 服务器
echo 'root:dev123' | chpasswd:设置 root 用户密码为 dev123
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config:允许 root 用户登录
echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config:启用密码认证
service ssh start:启动 SSH 服务
tail -f /dev/null:保持容器运行(重要!)
9.6.4 验证容器配置
等待配置完成:
1 2 3 4 5 6
| # 等待容器配置完成(约1-2分钟) echo "等待容器配置完成..." sleep 60
# 查看配置日志 sudo docker logs packer-dev
|
检查容器状态:
1 2 3 4 5 6 7
| # 检查容器是否正在运行 sudo docker ps | grep packer-dev # 应该看到:STATUS 为 "Up X minutes"
# 检查端口映射 sudo docker port packer-dev # 应该看到:22/tcp -> 0.0.0.0:2222
|
测试 SSH 连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| # 从 WSL 测试 SSH 连接 ssh root@localhost -p 2222
# 第一次连接会提示: # The authenticity of host '[localhost]:2222' can't be established. # 输入:yes
# 然后输入密码:dev123
# 连接成功后应该看到: root@container_id:/home/kfx_project/compress_pack/packer#
# 验证环境 pwd # 应该显示项目目录 ls -la # 应该看到 CMakeLists.txt, src/, libs/ which clang++ # 应该显示 OLLVM 编译器路径
# 退出测试连接 exit
|
9.6.5 配置 VSCode Remote-SSH
安装 Remote-SSH 扩展:
1 2 3 4
| # 1. 在 VSCode 中按 Ctrl+Shift+X(打开扩展面板) # 2. 搜索 "Remote - SSH" # 3. 安装 Microsoft 的 "Remote - SSH" 扩展 # 4. 等待安装完成(扩展图标会出现在左侧活动栏)
|
添加 SSH 主机:
1 2 3 4 5
| # 1. 在 VSCode 中按 Ctrl+Shift+P(打开命令面板) # 2. 输入 "Remote-SSH: Add New SSH Host" # 3. 在弹出的输入框中输入:ssh root@localhost -p 2222 # 4. 选择 SSH 配置文件(通常选择第一个,如 C:\Users\YourName\.ssh\config) # 5. 看到 "Host added!" 提示表示添加成功
|
连接到 Docker 容器:
1 2 3 4 5 6 7
| # 1. Ctrl+Shift+P # 2. 输入 "Remote-SSH: Connect to Host" # 3. 选择 "root@localhost" 或 "localhost" # 4. 选择 "Linux" 作为平台类型 # 5. 如果提示信任主机,选择 "Continue" # 6. 输入密码:dev123 # 7. 等待连接建立(首次连接可能需要1-2分钟)
|
连接成功的标志:
9.6.6 配置开发环境
打开项目目录:
1 2 3 4 5
| # 在连接成功的 VSCode 窗口中: # 1. File → Open Folder # 2. 输入:/home/kfx_project/compress_pack/packer # 3. 点击 OK # 4. 如果提示信任文件夹,点击 "Yes, I trust the authors"
|
安装必要的扩展:
1 2 3 4 5 6 7
| # 在 SSH 连接的 VSCode 窗口中安装扩展: # 1. Ctrl+Shift+X 打开扩展面板 # 2. 搜索并安装以下扩展: # - C/C++ (Microsoft) # - CMake Tools (Microsoft) # - C/C++ Extension Pack (Microsoft) # 3. 等待扩展安装完成
|
配置 CMake Tools:
1 2 3 4 5 6
| # 1. 按 Ctrl+Shift+P # 2. 输入 "CMake: Select a Kit" # 3. 选择 "Unspecified" 或手动配置的编译器 # 4. 按 Ctrl+Shift+P # 5. 输入 "CMake: Configure" # 6. 等待配置完成
|
9.6.7 验证开发环境
检查终端环境:
1 2 3 4 5 6 7 8 9 10
| # 在 VSCode 中按 Ctrl+`(打开集成终端) # 应该看到: root@container_id:/home/kfx_project/compress_pack/packer#
# 验证环境变量和工具 pwd # 应该显示项目根目录 ls -la # 应该看到项目文件 which cmake # 检查 CMake 路径 which clang++ # 检查编译器路径 cmake --version # 检查版本
|
测试编译功能:
1 2 3 4 5 6 7 8 9
| # 在 VSCode 终端中测试编译 mkdir -p build && cd build cmake .. make 2>&1 | grep -i error
# 或者使用 VSCode 的 CMake 功能: # 1. Ctrl+Shift+P # 2. 输入 "CMake: Build" # 3. 等待编译完成
|
验证 IntelliSense 功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <iostream> #include <string>
int main() { std::string test = "hello"; std::cout << test << std::endl; return 0; }
|
9.6.8 日常开发工作流程
启动开发环境:
1 2 3 4 5 6 7 8
| # 1. 检查容器是否运行 docker ps | grep packer-dev
# 2. 如果容器停止了,启动它 docker start packer-dev
# 3. 在 VSCode 中连接 # Ctrl+Shift+P → "Remote-SSH: Connect to Host" → 选择 localhost
|
编译和调试:
1 2 3 4 5 6 7
| # 方法1:使用 VSCode 的 CMake Tools # - 底部状态栏点击 "Build" 按钮 # - 或 Ctrl+Shift+P → "CMake: Build"
# 方法2:使用终端命令 # - Ctrl+` 打开终端 # - cd build && make
|
文件编辑:
1 2 3 4 5
| # VSCode 中的文件修改会实时同步到 WSL # 1. 在 VSCode 中编辑文件 # 2. Ctrl+S 保存 # 3. 文件自动同步到 WSL 的原始目录 # 4. 在容器中立即可见更改
|
9.6.9 容器管理脚本
创建管理脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| # 在 WSL 的项目目录中创建 docker-dev.sh cat > docker-dev.sh << 'EOF' #!/bin/bash
CONTAINER_NAME="packer-dev" IMAGE_NAME="ollvm_dev_rel" PROJECT_PATH="/home/trg/share_work" WORK_DIR="/home/kfx_project/compress_pack/packer" SSH_PORT="2222" PASSWORD="dev123"
case "$1" in start) echo "=== 启动开发容器 ===" if docker ps | grep -q $CONTAINER_NAME; then echo "容器已在运行" elif docker ps -a | grep -q $CONTAINER_NAME; then echo "启动现有容器..." docker start $CONTAINER_NAME else echo "创建新容器..." docker run -d \ --name $CONTAINER_NAME \ -p $SSH_PORT:22 \ -v $PROJECT_PATH:/home \ -w $WORK_DIR \ $IMAGE_NAME \ bash -c " apt update && \ apt install -y openssh-server vim nano tree htop && \ echo 'root:$PASSWORD' | chpasswd && \ echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config && \ echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config && \ service ssh start && \ tail -f /dev/null " echo "等待容器配置完成..." sleep 30 fi echo "容器已就绪" echo "SSH 连接: ssh root@localhost -p $SSH_PORT" echo "密码: $PASSWORD" ;; stop) echo "=== 停止开发容器 ===" docker stop $CONTAINER_NAME echo "容器已停止" ;; remove) echo "=== 删除开发容器 ===" docker stop $CONTAINER_NAME 2>/dev/null docker rm $CONTAINER_NAME 2>/dev/null echo "容器已删除" ;; status) echo "=== 容器状态 ===" docker ps -a --filter "name=$CONTAINER_NAME" echo "" echo "端口映射:" docker port $CONTAINER_NAME 2>/dev/null || echo "容器未运行" ;; logs) echo "=== 容器日志 ===" docker logs $CONTAINER_NAME ;; shell) echo "=== 进入容器 Shell ===" docker exec -it $CONTAINER_NAME bash ;; ssh-test) echo "=== 测试 SSH 连接 ===" echo "连接地址: ssh root@localhost -p $SSH_PORT" echo "密码: $PASSWORD" ssh root@localhost -p $SSH_PORT ;; *) echo "Docker 开发环境管理脚本" echo "" echo "用法: $0 {start|stop|remove|status|logs|shell|ssh-test}" echo "" echo "命令说明:" echo " start - 启动或创建开发容器" echo " stop - 停止开发容器" echo " remove - 删除开发容器" echo " status - 查看容器状态" echo " logs - 查看容器日志" echo " shell - 直接进入容器 Shell" echo " ssh-test - 测试 SSH 连接" echo "" echo "VSCode 连接信息:" echo " 主机: localhost" echo " 端口: $SSH_PORT" echo " 用户: root" echo " 密码: $PASSWORD" ;; esac EOF
# 给脚本执行权限 chmod +x docker-dev.sh
|
使用管理脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| # 启动开发环境 ./docker-dev.sh start
# 查看容器状态 ./docker-dev.sh status
# 测试 SSH 连接 ./docker-dev.sh ssh-test
# 查看容器日志 ./docker-dev.sh logs
# 停止容器 ./docker-dev.sh stop
|
9.6.10 VSCode 配置优化
创建 VSCode 工作区配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| { "cmake.configureArgs": [ "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" ], "cmake.buildDirectory": "${workspaceFolder}/build", "C_Cpp.default.compilerPath": "/root/OLLVM_17/build/bin/clang++", "C_Cpp.default.cppStandard": "c++17", "C_Cpp.default.cStandard": "c17", "C_Cpp.default.intelliSenseMode": "clang-x64", "C_Cpp.default.defines": [ "_WIN32", "WIN32", "_WIN64", "WIN64", "_WINDOWS", "_DEBUG" ], "files.associations": { "*.h": "c", "*.hpp": "cpp", "*.cpp": "cpp", "CMakeLists.txt": "cmake" }, "editor.tabSize": 4, "editor.insertSpaces": true }
|
创建调试配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| { "version": "0.2.0", "configurations": [ { "name": "Debug Packer", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/x64/Debug/packer.exe", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }
|
9.6.11 常见问题和解决方案
问题1:SSH 连接被拒绝
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| # 错误:ssh: connect to host localhost port 2222: Connection refused
# 解决步骤: # 1. 检查容器是否运行 docker ps | grep packer-dev
# 2. 检查 SSH 服务状态 docker exec -it packer-dev service ssh status
# 3. 重启 SSH 服务 docker exec -it packer-dev service ssh restart
# 4. 重新测试连接 ssh root@localhost -p 2222
|
问题2:密码认证失败
1 2 3 4 5 6 7 8 9 10 11
| # 错误:Permission denied (publickey,password)
# 解决步骤: # 1. 重新设置密码 docker exec -it packer-dev bash -c "echo 'root:dev123' | chpasswd"
# 2. 检查 SSH 配置 docker exec -it packer-dev grep -E "(PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config
# 3. 重启 SSH 服务 docker exec -it packer-dev service ssh restart
|
问题3:VSCode 连接后看不到项目文件
1 2 3 4 5 6 7 8 9
| # 解决步骤: # 1. 检查工作目录 # 在 VSCode 终端中:pwd
# 2. 手动切换到项目目录 cd /home/kfx_project/compress_pack/packer
# 3. 重新打开正确的文件夹 # File → Open Folder → /home/kfx_project/compress_pack/packer
|
问题4:编译器找不到
1 2 3 4 5 6
| # 在 VSCode 终端中检查: which clang++ ls -la /root/OLLVM_17/build/bin/
# 如果编译器不存在,检查镜像是否正确 docker images | grep ollvm_dev_rel
|
9.6.12 开发环境维护
定期维护任务:
1 2 3 4 5 6 7 8 9 10 11
| # 1. 清理停止的容器 docker container prune
# 2. 清理未使用的镜像 docker image prune
# 3. 查看磁盘使用情况 docker system df
# 4. 备份重要配置 cp .vscode/settings.json .vscode/settings.json.bak
|
容器数据备份:
1 2 3 4 5 6 7 8
| # 1. 提交容器为新镜像(保存环境配置) docker commit packer-dev packer-dev-configured
# 2. 导出镜像 docker save packer-dev-configured > packer-dev-backup.tar
# 3. 恢复镜像 docker load < packer-dev-backup.tar
|
环境重置:
1 2 3
| # 完全重置开发环境 ./docker-dev.sh remove # 删除容器 ./docker-dev.sh start # 重新创建
|
9.6.13 最佳实践总结
开发流程最佳实践:
一次性设置:创建配置好的开发容器
持久化使用:使用命名容器,避免重复配置
代码分离:代码在 WSL 中,环境在 Docker 中
工具集成:充分利用 VSCode 的开发功能
脚本管理:使用脚本简化日常操作
安全注意事项:
🔒 使用强密码或 SSH 密钥认证
🔒 不要在生产环境中使用简单密码
🔒 定期更新容器中的软件包
🔒 及时清理不需要的容器和镜像
性能优化建议:
⚡ 使用 SSD 存储提高 I/O 性能
⚡ 分配足够的内存给 Docker
⚡ 使用多核编译:make -j$(nproc)
⚡ 启用 CMake 并行构建
通过这套完整的 VSCode + SSH + Docker 开发环境,你可以享受到现代 IDE 的所有功能,同时保持环境的隔离性和可重复性,非常适合交叉编译和团队协作开发。