这是B战up主Xiaobing1016的课程笔记,详细内容见 基于VSCode和CMake实现C/C++开发。在此感谢up主的无私分享和细心讲解。
pwd - Print working directory
作用:打印当前终端所在目录
用法:pwd
# 打印当前目录
pwd
ls - List
作用:列出当前目录下的所有文件/文件夹名称
用法:ls [选项] [路径]
# 当前目录下所有文件/文件夹名
ls
# 指定路径下
ls ../
ls /home
# ls 选项 路径
# -l:表示以详细列表的形式展示
# -a:显示隐藏文件/文件夹
# -h:以可读性较高的形式展示
ls -lah /home
cd - change directory
作用:切换当前目录
mkdir - make directory
作用:创建目录
# 创建目录
mkdir build
# 创建多层目录
mkdir -p ./build/bin
# 创建多个目录
mkdir include src build
touch - create file
作用:创建新文件
# 在当前目录下创建文件
touch CmakeLists.txt
# 在指定目录下创建文件
touch ../CmakeLists.txt
rm - remove files or directories
作用:删除文件/目录
# 删除当前目录下的文件
rm main.cpp
# 删除指定目录下的文件
rm ../main.cpp
# 删除当前目录下的文件夹
rm -rf build
# 删除指定目录下的文件夹
rm -rf ../build
cp - copy files or directories
作用:拷贝文件/文件夹到指定位置
# 复制文件到指定位置
cp ../CmakeLists.txt ./
# 复制文件夹到指定位置
cp -r ../build ./
mv - move(rename) files or directories
作用:移动文件/文件夹到指定位置,或重命名
# 移动当前目录下文件到指定目录下
mv CmakeLists.txt ../CmakeLists.txt
# 移动当前目录下文件夹到指定目录
mv build ../build
# 移动并重命名
mv CmakeLists.txt ../CmakeLists_1.txt
-g 编译带调试信息的可执行文件
g++ -g test.cpp
-O[n] 优化源代码,使其执行速度更快
# -O 减小代码长度和执行时间
# -O0 不做优化
# -O1 默认优化,与-O一样
# -O2 在-O1优化基础上,进行额外调整
# -O3 最高级别优化
g++ -O3 test.cpp
-l和-L 指定库文件 | 指定库文件路径
# -l 指定要链接的库,/usr/lib和/usr/local/lib中的库可直接用-l链接
g++ -lglog test.cpp
# -L 对于不在系统默认库路径里的库,就需要使用-L指定库文件路径
g++ -L/Thirdparty/Open3D/lib -lOpen3D test.cpp
-I 指定头文件搜索目录
# -I 添加库的头文件目录,系统默认头文件搜索路径为/usr/include和/usr/local/include
g++ -I/Thirdparty/Open3D/include test.cpp
-std=c++11 设置C++11编译标准
-o <输出文件名> 指定输出文件名
-D 定义宏
# 常用-DDEBUG: 定义DEBUG宏,打印调试信息
g++ -DDEBUG test.cpp
要使用gdb调试,编译时需要添加-g参数。在终端中执行gdb [可执行文件]
,进入gdb调试程序。
## 括号内为命令的简化使用
$(gdb)help(h) # 查看命令帮助
$(gdb)run(r) # 开始运行文件
$(gdb)run argv[1] argv[2] # 调试时命令行传参
$(gdb)start # 单步执行,运动程序,停在第一行语句
$(gdb)list(l) # 查看源代码
$(gdb)set # 设置变量的值
$(gdb)next(n) # 逐过程调试,函数直接执行
$(gdb)step(s) # 逐语句调试,跳入函数内部执行
$(gdb)backtrace(bt) # 查看函数调用的堆栈
$(gdb)continue(c) # 继续
$(gdb)print(p) # 打印变量
$(gdb)display # 追踪查看变量
$(gdb)undisplay # 取消追踪查看变量
$(gdb)watch # 被设置观察点的变量发生修改时,打印显示
$(gdb)break n # 在第n行设置断点
$(gdb)info breakpoints # 查看设置的断点
$(gdb)delete breakpoints n # 删除第n个断点
$(gdb)enable breakpoints # 启用断点
$(gdb)disable breakpoints # 禁用断点
cmake_minimum_required 指定CMake的最小版本要求
cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR)
project 定义工程名,并可指定工程支持的语言
project(Geometry)
set 显示定义变量
set(SRC src/point_cloud.cpp main.cpp)
include_directories 向工程添加多个特定的头文件搜索路径 —>相当于g++中的-I
include_directories(/home/Open3D/include ${PROJECT_SOURCE_DIR}/include)
link_directories 向工程添加多个特定的库文件搜索路径 —> 相当于g++中的-L
link_directories(/home/Open3D/lib)
add_library 生成库文件
# 通过变量SRC中的文件生成libhello.so动态库
add_library(hello SHARED ${SRC})
add_complie_options 添加编译参数
# 添加C++11标准、显示警告、-O2优化
add_compile_options(-Wall -std=c++11 -O2)
add_executable 生成可执行文件
add_executable(test ${SRC})
target_link_libraries 为target添加需要链接的动态库 —> 相当于g++中的-l
target_link_libraries(test hello)
add_subdirectory 向当前工程添加存放源文件的子目录,并可指定中间和目标二进制文件存放的位置
# 添加src目录,src中需要有CMakeLists.txt
add_subdirectory(src)
list 对列表进行操作
# 添加工程目录中的cmake文件夹到搜索路径,方便后续按照文件夹中的.cmake文件查找对应的外部库
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
## find_package(OpenCV REQUIRED)
CMAKE_C_FLAGS、CMAKE_CXX_FLAGS gcc、g++编译选项
set(CMAKE_CXX_FLAGS "{CMAKE_CXX_FLAGS} -std=c++11") # 添加-std=c++11
CMAKE_BUILD_TYPE 编译类型(Debug, Release)
# 调试时选择Debug,会自动加上-g
set(CMAKE_BUILD_TYPE Debug)
# 发布时选择Release,会自动加上-O3,关闭debug调试
set(CMAKE_BUILD_TYPE Release)
CMAKE_BINARY_DIR, PROJECT_BINARY_DIR,
在out-of-source编译(新建build并在build中cmake …)中,上述三个变量都指代build目录。
CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR,
上述三个变量都指代工程根目录
EXECUTABLE_OUTPUT_PATH(旧), CMAKE_RUNTIME_OUTPUT_DIRECTORY 可执行文件输出的存放路径
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
LIBRARY_OUTPUT_PATH(旧), CMAKE_LIBRARY_OUTPUT_DIRECTORY 库文件输出的存放路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
CMAKE_CXX_STANDARD C++标准
set(CMAKE_CXX_STANDARD 11) # 添加c++11支持,和设置编译选项效果一样
include/
Gun.h
Soldier.h
src/
Gun.cpp
Soldier.cpp
bin/
main.cpp
CMakeLists.txt
编写源文件
构建编译规则CmakeLists.txt
cmake_minimum_required(VERSION 3.2.0)
project(SoldierFire)
## 启动调试,也可写为set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "{CMAKE_CXX_FLAGS} -g -Wall")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
add_executable(soldier_fire main.cpp src/Gun.cpp src/Soldier.cpp)
target_include_directories(sodier_fire PUBLIC ${PROJECT_SOURCE_DIR}/include)
配置json文件并调试项目
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/soldier_fire", // 步骤1 修改可执行文件路径
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
"preLaunchTask": "Build", // 步骤2 配置自动化生成任务
}
]
{
"version": "2.0.0",
"options": {
"cwd": "${workspaceFolder}/build"
},
"tasks": [
{
"label": "cmake",
"type": "shell",
"command": "cmake",
"args": [
".."
]
},
{
"label": "make",
"group": {
"kind": "build",
"isDefault": true
},
"command": "make",
"args": []
},
{
"label": "Build",
"dependsOrder": "sequence", //按顺序执行任务依赖项
"dependsOn":[
"cmake",
"make"
]
}
]
}
现代CMake是基于target构建项目的,target可以是一个executable,也可以是一个static/shared/header-only lib,甚至是一个custom。
add_executable( ...)
add_library( [SHARED|STATIC|INTERFACE] ...)
add_custom_target( ...) # 伪目标
其中,对于lib target,有:
add_library( [SHARED|STATIC|INTERFACE] ...)
BUILD_SHARED_LIBS
SHARED
—— creates a shared librarySTATIC
—— creates a static libraryINTERFACE
—— creates a header only libraryadd_library( ALIAS )
add_library( IMPORTED [GLOBAL])
可以使用如下函数来控制target:
target_include_directories( [PUBLIC|INTERFACE|PRIVATE] ...)
target_compile_definitions( [PUBLIC|INTERFACE|PRIVATE] ...)
target_compile_options( [PUBLIC|INTERFACE|PRIVATE]
target_sources( [PUBLIC|INTERFACE|PRIVATE] ...)
target_link_directories( [PUBLIC|INTERFACE|PRIVATE] ...)
其中,函数中的[PUBLIC|INTERFACE|PRIVATE]表示作用域(传递),假设项目中存在如下3个targets——库hello.so,hello_world.so,可执行文件main.o。
PRIVATE
只有当前构建的hello_world需要使用hello的功能,而main只使用hello_world中的功能,即用户只能使用hello_world中定义的功能。此时,hello_world/CMakeLists.txt 中使用 PRIVATE 关键字。INTERFACE
当前构建的hello_world不使用hello的功能,而main会使用hello的功能,即hello_world仅仅起到一个传递依赖的作用。此时hello_world/CMakeLists.txt中使用INTERFACE关键字。PUBLIC
当前构建的hello_world和main都依赖hello,即用户可以同时使用hello_world和hello的功能。此时,hello_world/CMakeLists.txt中使用PUBLIC。详解target_**中的PUBLIC、PRIVATE、INTERFACE
调用环境变量
使用ENV{variable_name}
调用系统的环境变量。
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
主要开关选项
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # 使用c++11编译
添加target
# GLOB_RECURSE表示递归搜索,CONFIGURE_DEPENDS表示自动检测目录更新
file(GLOB_RECURSE SRCS CONFIGURE_DEPENDS src/*.cpp include/*.h)
add_library(biology STATIC ${SRCS})
target_include_directories(biology PUBLIC include)
源码依赖
include(FetchContent)
FetchContent_Declare(json
GIT_REPOSITORY https://gitee.com/slamist/json.git
GIT_TAG v3.7.3)
FetchContent_MakeAvailable(json)
添加第三方库
find_package
命令包含两种模式:Module模式和Config模式。Module模式会查找Find
,Config模式会查找
。缺省时先查找Find
,再查找
。用法如下:
find_package( [version]
[EXACT] [QUIET] [REQUIRED]
[CONFIG] [MODULE]
[COMPONENTS [components...]]
)
每个Find
文件会定义如下变量:
_INCLUDE_DIRS
:库的头文件目录_LIBRARIES
:库的库文件_DEFINITIONS
:使用XX库时用到的预处理定义_FOUND
:是否找到库对于CONFIG模式而言,直接使用find_package即可添加完整的库依赖;而对于MODULE模式,由于历史兼容性,可能需要额外做包含头文件目录的操作。
find_package(OpenCV REUIQRED)
target_link_libraries(MyTarget ${OpenCV_LIBS})
添加预处理宏
用于添加一些可选的依赖,例如使用TBB进行并行化操作:
#ifdef WITH_TBB
#include
#endif
int main() {
#ifdef WITH_TBB
tbb::parallel_for(0, 4, [&] (int i) {
#else
for (int i = 0; i < 4; ++i) {
#endif
printf("hello, %d \n", i);
#ifdef WITH_TBB
});
#else
}
#endif
}
在CMakeLists.txt中,通过target_compile_definitions
命令传递宏给编译器:
//----CMakeLists.txt----//
find_package(TBB)
if (TBB_FOUND)
message(STATUS "TBB found at: ${TBB_DIR}")
target_link_libraries(MyTarget PUBLIC TBB::tbb)
target_compile_definitions(MyTarget PUBLIC WITH_TBB)
else()
message(WARNING "TBB not found")
endif()
定义生成目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) # so
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # executable
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) # a
添加伪目标
add_custom_target
可用于创建伪目标。例如,创建一个run伪目标,执行main,从而在build时启动main.exe程序。
add_executable(main main.cc)
add_custom_target(run COMMAND $)
安装库
为了将自己写好的库能够安装到系统中,给用户使用(通过find_package调用),可以通过CMake进行安装配置。
我们定义一个库项目LearningCMake,其中包括MyLib库和可执行文件MyExe,其项目结构如下:
LearningCMake
├── cmake
│ └── MyLibConfig.cmake.in
├── CMakeLists.txt
├── MyExe
│ ├── CMakeLists.txt
│ └── src
│ └── main.cc
└── MyLib
├── CMakeLists.txt
├── include
│ └── MyLib
│ └── MyLib.h
└── src
└── MyLib.cc
1、首先在顶层CMakeLists.txt中设置好安装路径:
#---- CMakeLists.txt ----#
cmake_minimum_required(VERSION 3.6)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
project(LearningCMake VERSION 0.1.0)
message(STATUS "Project will be install to ${CMAKE_INSTALL_PREFIX}")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}")
# Set installation paths
if(UNIX OR CYGWIN)
include(GNUInstallDirs)
set(MyLib_INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}")
set(MyLib_INSTALL_BIN_DIR "${CMAKE_INSTALL_BINDIR}")
set(MyLib_INSTALL_LIB_DIR "${CMAKE_INSTALL_LIBDIR}")
set(MyLib_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
else()
set(MyLib_INSTALL_INCLUDE_DIR include)
set(MyLib_INSTALL_BIN_DIR bin)
set(MyLib_INSTALL_LIB_DIR lib)
set(MyLib_INSTALL_CMAKE_DIR CMake)
endif()
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# Allow the developer to select if Dynamic or Static libraries are built
option (BUILD_SHARED_LIBS "Build Shared Libraries" ON)
set (LIB_TYPE STATIC)
if (BUILD_SHARED_LIBS)
set (LIB_TYPE SHARED)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
add_subdirectory(MyLib)
option(BUILD_TEST "Build test." ON)
if (BUILD_TEST)
add_subdirectory(MyExe)
endif()
2、在MyLib目录中,CMakeLists.txt配置安装:
#---- MyLib/CMakeLists.txt ----#
file(GLOB_RECURSE SRCS CONFIGURE_DEPENDS src/*.cc include/*.h)
add_library(MyLib ${LIB_TYPE} ${SRCS})
target_include_directories(MyLib
PUBLIC
$
$
)
# <<< Install and export targets >>>
# Install targets
install(
TARGETS MyLib
EXPORT MyLibTargets
ARCHIVE
DESTINATION ${MyLib_INSTALL_LIB_DIR}
COMPONENT lib
RUNTIME
DESTINATION ${MyLib_INSTALL_BIN_DIR}
COMPONENT bin
LIBRARY
DESTINATION ${MyLib_INSTALL_LIB_DIR}
COMPONENT lib
PUBLIC_HEADER
DESTINATION ${MyLib_INSTALL_INCLUDE_DIR}/MyLib
COMPONENT dev
)
# Install config files
include(CMakePackageConfigHelpers)
# find_package MyLib Version
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
# find_package MyLib
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/MyLibConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake
INSTALL_DESTINATION ${MyLib_INSTALL_CMAKE_DIR}
)
# Install the MyLibConfig.cmake and MyLibConfigVersion.cmake
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake
DESTINATION ${MyLib_INSTALL_CMAKE_DIR}
COMPONENT dev
)
# Creates export file which can be imported by other cmake projects
install(EXPORT MyLibTargets
NAMESPACE MyLib::
DESTINATION ${MyLib_INSTALL_CMAKE_DIR})
3、cmake目录中需要设置MyLibConfig.cmake.in,用于生成MyLibConfig.cmake文件,以便用户通过find_package添加MyLib库。
@PACKAGE_INIT@
if(POLICY CMP0072)
cmake_policy(SET CMP0072 @CMP0072_VALUE@)
endif()
# Declare the dependencies of MyLib
# include(CMakeFindDependencyMacro)
# find_dependency()
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/MyLibTargets.cmake")