简介
VS code环境下使用CMake构建工程
- 导入VScode cmake工程
- C/C++多文件工程构建
- 制作静态/动态链接库文件
- 使用外部库文件构建工程
- CMake常用指令
- 填坑
本文主要介绍vscode环境下使用CMake构建工程的详细用法,包含多文件工程的构建,静态链接库/动态链接库文件的生成,外部库文件的使用。
0.导入VScode cmake工程
首先创建一个C/C++工程文件夹,用VSCode打开,目录结构如下:
1、使用快捷键Ctrl+Shift+P打开vscode 的控制面板。输入CMake:quick start
2、随后在弹出的窗口中选择GCC,如果没有弹出,则按住Ctrl+Shift+P,输入CMake: Select a kit 后选择
如果选错了,可以用以下方式更换
3 .编译运行
补充: 如需要,断点debug调试步骤
单步运行
1. C/C++多文件工程构建
工程目录结构如下,将头文件放在include中,.c文件放在src目录下,每一个包含源文件(.c/.cpp)的文件夹如果工程需要使用,则都需要通过add_subdirectory
的方式添加到工程中。项目的根目录下需要有一个CMakeLists.txt
,用来配置工程信息,建立和子目录的联系,每一个包含源码的子目录也都需要一个CMakeLists.txt
。
D:.
│ CMakeLists.txt
│ main.c
├─build
├─include
│ hello.h
└─src
CMakeLists.txt
hello.c
在工程目录下mian.c
文件中调用src
文件下的hello.c
文件中的函数
将hello.h
文件放在include
文件夹中
工程中的CMakeLists.txt
文件如下
cmake_minimum_required(VERSION 3.0.0)
project(multiFileTest VERSION 0.1.0)
include(CTest)
enable_testing()
#包含头文件
include_directories(include)
#工程目录下的原文件,命名为ROOT_SOURCE
aux_source_directory(. ROOT_SOURCE)
#指定一个子目录src,制定了src,cmake才回去src中找CMakeList
add_subdirectory(src)
#添加可执行文件,生成一个目标文件,取名为hello
add_executable(hello ${ROOT_SOURCE} ${HELLO_SRC})
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
src
子目录下的CMakeLists.txt
内容如下
#子目录下的源文件,取名为HELLO_SRC,让父级目录可见
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} HELLO_SRC)
set(HELLO_SRC ${HELLO_SRC} PARENT_SCOPE)
2. 制作静态/动态链接库文件
工程目录结构如下
D:.
│ CMakeLists.txt
├─build
└─lib
CMakeLists.txt
myhello.c
myhello.h
把hello.c
和hello.h
编译成静态和动态链接库文件,供外部使用
工程目录下的CMakeLists.txt
内容如下,只用包含注释的两句话是必要的,后面的install可安装也可不安装:
cmake_minimum_required(VERSION 3.0.0)
#工程设置
project(myhello VERSION 0.1.0)
include(CTest)
enable_testing()
#包含源文件,生成的可执行文件放在bin文件中
add_subdirectory(lib bin)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
install(FILES hello.h DESTINATION include/hello)
install(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
lib目录下的CMakeLists.txt
内容如下,同时生成动态库和静态库:
set(LIB_SRC myhello.c)
add_library(hello_static STATIC ${LIB_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello" )
set_target_properties( hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1 )
add_library(hello SHARED ${LIB_SRC})
set_target_properties(hello PROPERTIES OUTPUT_NAME "hello")
set_target_properties( hello PROPERTIES CLEAN_DIRECT_OUTPUT 1 )
这个实例是使用C文件做的库,在C++中调用,.c和.h文件内容如下
//.c文件如下
#include "myhello.h"
#include <stdio.h>
void helloFun()
{
printf("helloWorld!");
}
//.h文件如下
#ifndef _MYHELLO_H
#define _MYHELLO_H
#ifdef __cplusplus
extern "C"{
#endif
extern void helloFun(void);
#ifdef __cplusplus
}
#endif
#endif
编译链接之后生成了libhello.a
和libhello.dll
3. 调用外部库文件构建工程
工程目录结构如下:
D:.
│ CMakeLists.txt
│ main.cpp
├─build
├─include
│ myhello.h
└─lib
libhello.a
libhello.dll
工程目录下的main.cpp内容如下:
#include <iostream>
#include "myhello.h"
//如果是windows环境下需要指明库的位置
#pragma comment(lib, "lib/libhello.lib")
int main(int, char**) {
helloFun();
}
注意:生成的可执行文件必须和库文件放一起,才能正常运行
。
工程目录下的CMakeLists.txt
内容如下:
cmake_minimum_required(VERSION 3.0.0)
project(mytest VERSION 0.1.0)
include(CTest)
enable_testing()
include_directories(include)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
LINK_DIRECTORIES(lib)
add_executable(mytest main.cpp)
target_link_libraries(mytest libhello.dll)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
4. CMake常用指令
#设置最低cmake版本
#该指令指定cmake的最低版本,如果低于指定版本,则出现错误,整个过程终止
cmake_minimum_required(VERSION 3.0.0)
#配置项目信息
#设置项目名称
project(mytest VERSION 0.1.0)
#搜索当前目录下所有源文件
#原型:AUX_SOURCE_DIRECTORY(dir VARIABLE)
#该指令用于发现一个目录下所有的源代码文件并将列表存储在一个变量中
aux_source_directory(. ROOT_SOURCE)
# 包含头文件 (具有全局效果)
# 给工程添加头文件,例如使用opencv时需要包含/usr/local/include/opencv/cv.h这个头文件,则我们需要
# include_directories(/usr/local/include),在调用的函数里写 #include “opencv/cv.h”即可
include_directories(include)
#为指定的目标添加搜索头文件
# target_include_directories(<target> <PUBLIC|INTERFACE|PRIVATE]> <items>):为特定的目标链接头文件目录。
# 我们将${OpenCV_Include_dir}头文件库路径只添加到了mytest项目
target_include_directories(mytest PUBLIC ${OpenCV_Include_dir})
#包含链接库目录
LINK_DIRECTORIES(lib)
#包含工程子目录
#该指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
#原型: ADD_SUBDIRECTORY(source_dir [binary_dir]
add_subdirectory(src)
#生成可执行文件
#原型: ADD_EXECUTABLE(程序名 源代码文件) or add_executable(可执行文件名 1.cpp 2.ppp …)
add_executable(mytest main.cpp)
#生成静态链接库
#原型:ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
add_library(hello_static STATIC ${LIB_SRC})
#生成动态链接库
add_library(hello SHARED ${LIB_SRC})
#链接库文件,注意链接库文件必须在生成可执行文件之后
#为库或者可执行文件添加需要链接的库
#原型:target_link_libraries (库/可执行文件 library1 library2 ...)
target_link_libraries(mytest libhello.dll)
#配置编译类型
#给变量赋值为参数,这里的变量可以CMake自己变量也可是自己定义的变量
#原型:set (变量 参数)
set(CMAKE_BUILD_TYPE Debug)
# 编译选项:
add_compile_options(-std=c++11)
# 如果想要指定具体的编译器的选项,可以使用make_cxx_flags()或cmake_c_flags()。
补充:cmake命令详细补充说明
1.set命令
set (变量 参数)
给变量赋值为参数,这里的变量可以CMake自己变量也可是自己定义的变量
CMake常用变量
-
CMAKE_BUILD_TYPE:CMake编译类型,常用参数如下
Debug: 调试模式,输出调试信息,不做优化
Release: 发布模式,没有调试信息,全优化
RelWithDebInfo::类似Release, 但包括调试信息
MinSizeRel: 一种特殊的Release模式,会特别优化库的大小例:
set (CMAKE_BUILD_TYPE Debug) 设置编译类型为Debug模式
-
CMAKE_CXX_FLAGS:对C++代码编译过程进行设置,常用参数如下:
–std=c++11 :支持C++11标准
-Wall :编译过程输出警告信息
-O3 :使用o3等级优化 注:优化等级越高,生成可执行文件越少,但可能会优化 掉一些需要的函数,如果实际测试发现一个应该执行到的函数没有执行可能是优化器给优化掉了,需要降低优化等级或者直接 -O0(不优化)
-march=native :加了该参数,编译器将自动检测CPU型号,并使用该型号的增强指令集,比如各个版本的SSE AVX等,编译时编译器将优先使用这些增强指令集替代原始指令集,达到更快的执行效率。但往往这块儿会存在一些问题。如果安装第三方库开启了-march=native,则调用第三方库的工程里必须也得加 -march=native,因为使用增强指令值时要求操作数需要按照一定格式在内存储存,如4字节对齐或者16字节对齐或者其他形式,如果不加-march=native操作数将采用默认形式,就会报错。
可执行文件的可移植性降低,如果电脑A使用-march=native编译,将可执行文件送给电脑B,如果电脑B和电脑A支持的增强指令集不一样,则会执行不了,电脑B需要使用源码进行重新编译生成可执行文件。
例:set(CMAKE_CXX_FLAGS " C M A K E C X X F L A G S − W a l l − O 3 − m a r c h = n a t i v e " ) 注:例子里添加 {CMAKE_CXX_FLAGS} -Wall -O3 -march=native") 注:例子里添加 CMAKECXXFLAGS−Wall−O3−march=native")注:例子里添加{CMAKE_CXX_FLAGS}表示在CMAKE_CXX_FLAGS原有的基础上新增-Wall -O3 -march=native这些指令,如果不加${CMAKE_CXX_FLAGS},则会覆盖掉CMAKE_CXX_FLAGS 原有的配置。
-
CMAKE_C_FLAGS:对C代码编译过程进行设置,用法类似于CMAKE_CXX_FLAGS
-
CMAKE_CXX_STANDARD C++标准,如set (CMAKE_CXX_STANDARD 11)
-
LIBRARY_OUTPUT_PATH :指定生成库输出路径,如SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
2.message命令
定义:
MESSAGE关键字主要用于向终端输出用户自定义的信息,主要包含三种信息
SEND_ERROR,产生错误,生成过程被跳过
STATUS,输出前缀为–的信息
FATAL_ERROR,立即终止所有cmake过程
功能:打印日志
例如: MESSAGE(STATUS “current platform: Windows”)
3.option的命令
功能:
cmake中option起到编译开关的作用,CMakeLists.txt中option以前的语句,变量按未定义处理,option之后的语句,变量才被定义。另外,注意,option命令定义的变量不影响c或c++源码中#ifdef或者#ifndef逻辑判断
原型:
option( “<help_text>” [value])
举例:
PROJECT(HELLO)
IF(TEST)
MESSAGE("1########TEST is defined,vlaue:${TEST}")
ELSE()
MESSAGE("1########TEST is not defined")
ENDIF()
#command option
option(TEST "test affect to code" ON)
ADD_SUBDIRECTORY(lib bin)
IF(TEST)
MESSAGE("2#######TEST is defined,vlaue:${TEST}")
ELSE()
MESSAGE("2#######TEST is not defined")
ENDIF()
build是cmake编译路径,为了便于在出错时清理缓存文件,执行命令如下
cd build
cmake .. #等效于 cmake -D TEST=ON ..
输入如下:
$ cmake -D TEST=OFF ..
$ cmake -D TEST=ON ..
5. 填坑
在vscode下使用CMake构建工程,使用cmake quick start之后,自动生成了main.cpp
文件,CMakeLists.txt
文件和build
文件夹,在构建工程的过程中不要删除build文件夹,如果删除之后重新建build文件夹,然后cmake…,则会出现错误:
The CMAKE_C_COMPILER:
cl
is not a full path and was not found in the PATH.
编译器找不到了,这是则需要重新配置cmake工程,右键CMakeLists.txt,清除项目重新配置即可
调试过程中,在build文件下编译链接,不要删除build文件夹的内容
使用外部链接库文件时,如果提示使用的函数未定义,检查目标文件是否链接库target_link_libraries
undefined reference to .......
如果提示找不到库,检查库文件路径是否包含,LINK_DIRECTORIES
cannot find -lhello ......
找不到头文件,检查头文件是否包含
No such file or directory
#include "myhello.h"
^~~~~~~~~~~
_link_libraries`
undefined reference to .......