cmake 笔记
2024-06-20 18:55 清晨、午后 阅读(22) 评论(0) 编辑 收藏 举报一、 一个完整的工程
给工程起个名字
加上这句:project(hello)
命令:project(<PROJECT-NAME> [LANGUAGES] [<language-name>...])
作用:定义工程名称, 设置几个变量的名字: PROJECT_NAME, PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR, PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR
, 高级用法请见参考链接2:CMake命令
告诉CMake我的构建目标
add_executable(${PROJECT_NAME} ${hello_src})
让CMake找到我的头文件
target_include_directories(${project_name} PRIVATE
${CMAKE_SOURCE_DIR}/include
)
让CMake找到我的源文件
target_sources(${project_name} PRIVATE
${SRC}
${QRC}
)
让CMake找到我的库文件
target_link_directories(${project_name} PRIVATE
${Qt_LIBSDIR}
)
告诉CMake我要链接哪个库文件
set(Qt_LIBS
Qt5::Core
Qt5::Gui
Qt5::Widgets
)
target_link_libraries(${project_name} PRIVATE
${Qt_LIBS}
)
设置编译、连接参数
target_compile_features(${project_name} PRIVATE cxx_std_11)
target_compile_options(${project_name} PRIVATE -g -Wall)
target_link_options(${project_name} PRIVATE -Wl, --warn-unresolved-symbol)
开始构建
通过以上步骤, 最后,在文件头部添加CMake版本检查,以我的环境为例,我的CMake版本是3.0,那么我在脚本最开始加上:
cmake_minimum_required ( VERSION 3.0)
完整的CMakeLists.txt如下所示:
cmake_minimum_required ( VERSION 3.0)
project(hello)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src ${hello_src})
add_executable(${PROJECT_NAME} ${hello_src})
target_link_libraries(${PROJECT_NAME} util)
set(CMAKE_CXX_COMPILER "clang++" ) # 显示指定使用的C++编译器
set(CMAKE_CXX_FLAGS "-std=c++11") # c++11
set(CMAKE_CXX_FLAGS "-g") # 调试信息
set(CMAKE_CXX_FLAGS "-Wall") # 开启所有警告
set(CMAKE_CXX_FLAGS_DEBUG "-O0" ) # 调试包不优化
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG " ) # release包优化
构建动态库、静态库
如果要构建动态库,将 add_executable(${PROJECT_NAME} ${hello_src}) 替换为 ADD_LIBRARY(${PROJECT_NAME} SHARED ${hello_src}) 即可
如果要构建静态库,则为 ADD_LIBRARY(${PROJECT_NAME} STATIC ${hello_src})
可以将目标名称修改如下
SET_TARGET_PROPERTIES (${PROJECT_NAME}_static PROPERTIES OUTPUT_NAME "hello")
也可以指定动态库版本
SET_TARGET_PROPERTIES (${PROJECT_NAME}PROPERTIES VERSION 1.2 SOVERSION 1)
二、 其他
1. cmake help
CMake Reference Documentation — CMake 3.27.9 DocumentationCMAKE_SOURCE_DIR:根cmaek文件所在路径
CMAKE_CURRENT_SOURCE_DIR :当前cmake文件所在路径
PROJECT_SOURCE_DIR :当前cmake文件所在路径
现代CMake工具的设计理念和使用
https://mp.weixin.qq.com/s/qIEsImvOWa3-nRx7Ik_R8g
全面的例子
https://gitee.com/li736069609/qt_demo/blob/master/gui/CMakeLists.txt
2. cmake 中定义宏,代码中使用
# 新的语法
add_compile_definitions(OPENCV_VERSION="1.0.0") // 代码中可以直接使用 OPENCV_VERSION,值为 1.0.0
add_compile_definitions(WITH_OPENCV2) // 代码中可以直接使用 WITH_OPENCV2 进行条件编译
或者
add_compile_definitions(OPENCV_VERSION="1.0.0" WITH_OPENCV2)
# 旧的语法
add_definitions(-DSOURCE_DIR="aaa") // 代码中的宏 SOURCE_DIR 就是字符串aaa
add_definitions(-DWITH_TEST) // 代码中可以使用 WITH_TEST 进行条件编译
3. 静态库的连接顺序
工程在链接阶段静态库是有顺序要求的,如果懒得理这些顺序,就用 -Wl,--start-group 和 -Wl,--end-group 括起来即可
target_link_libraries(
-Wl,--start-group
libX3.a
libX2.a
libX1.a
-Wl,--end-group
)
4. 大型工程中有用的几个设置
target_link_options(${project_name} PRIVATE -Wl, --no-undefined)
QMAKE_LFLAGS += -Wl,--no-undefined (qmake)
告诉链接器在链接过程中不允许有未定义的符号 (对动态库 so)
target_link_options(${project_name} PRIVATE -Wl, --warn-unresolved-symbol)
告诉链接器在链接过程中可以有未定义的符号 (对可执行程序)
target_link_options(${project_name} PRIVATE -Wl, --copy-dt-needed-entries)
当链接可执行文件时,依赖于libA.so,而libA.so又依赖于libB.so,而且可执行文件中还直接调用了libB.so中的函数,如果可执行文件没有显示声明连接libB.so,则会报错 DSO missing from command line,因为自从binutils 2.22版本以后,如果你在程序中使用了你依赖的动态库所依赖的动态库中的函数
时,你就必须显式的指定你依赖的动态库所依赖的动态库,
5 Debug 与 Release 版本控制
cmake .. -DCMAKE_BUILD_TYPE=Debug/Release
if (CMAKE_BUILD_TYPE)
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
target_compile_options (${PROJECT_NAME} PRIVATE -O0 -g -Wall)
elseif (${CMAKE_BUILD_TYPE} STREQUAL "Release")
target_compile_options (${PROJECT_NAME} PRIVATE -O2 -g -Wall)
else ()
target_compile_options (${PROJECT_NAME} PRIVATE -O2 -g -Wall)
endif ()
else ()
target_compile_options (${PROJECT_NAME} PRIVATE -O2 -g -Wall)
endif ()
6. find_package 使用
find_package( <name> [version] [EXACT] [QUIET] [NO_MODULE] [ [ REQUIRED | COMPONENTS ] [ componets... ] ] )
用来调用预定义在 CMAKE_MODULE_PATH 下的 Find<name>.cmake 模块。
(1)查找引用使用第三方库,如使用 curl 库
在工程根目录中建立 src 文件夹,并在其下创建 main.c 文件,内容如下:
#include <curl/curl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> FILE *fp; int write_data(void *ptr, size_t size, size_t nmemb, void *stream) { int written = fwrite(ptr, size, nmemb, (FILE *)fp); return written; } int main() { const char * path = “/tmp/curl-test”; const char * mode = “w”; fp = fopen(path,mode); curl_global_init(CURL_GLOBAL_ALL); CURLcode res; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, “http://www.linux-ren.org”); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); res = curl_easy_perform(curl); curl_easy_cleanup(curl); }
在工程根目录下建立主工程文件 CMakeLists.txt,内容如下
PROJECT(CURLTEST) ADD_SUBDIRECTORY(src)
在 src 下创建 CMakeLists.txt
ADD_EXECUTABLE(curltest main.c)
现在自然是没办法编译的,我们需要添加 curl 的头文件路径和库文件。
方法 1:
直接通过 INCLUDE_DIRECTORIES 和 TARGET_LINK_LIBRARIES 指令添加:
我们可以直接在 src/CMakeLists.txt 中添加:
INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
然后建立 build 目录进行外部构建即可。
方法 2
使用 FindCURL 模块。
向src/CMakeLists.txt 中添加:
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)
对于系统预定义的 Find<name>.cmake 模块,使用方法一般如上例所示:
每一个模块都会定义以下几个变量
<name>_FOUND
<name>_INCLUDE_DIR or <name>_INCLUDES
<name>_LIBRARY or <name>_LIBRARIES
你可以通过<name>_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。
如果<name>_FOUND 为真,则将<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,将<name>_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。
(2)自定义 cmake 模块,如编写属于自己的 FindHello 模块。
定义 cmake/FindHELLO.cmake 模块
FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello) FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib) IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) SET(HELLO_FOUND TRUE) ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
IF (HELLO_FOUND) IF (NOT HELLO_FIND_QUIETLY) MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}") ENDIF (NOT HELLO_FIND_QUIETLY) ELSE (HELLO_FOUND) IF (HELLO_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find hello library") ENDIF (HELLO_FIND_REQUIRED) ENDIF (HELLO_FOUND)
可以看到在该模块中定义了 HELLO_FOUND、HELLO_INCLUDE_DIR、HELLO_LIBRARY变量
前面的 curl 例子中我们使用了最简单的 FIND_PACKAGE 指令,其实他可以使用多种参数
QUIET 参数,对应与我们编写的 FindHELLO 中的 HELLO_FIND_QUIETLY ,如果不指定这个参数,就会执行:
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
REQUIRED 参数,对应于 FindHELLO.cmake 模块中的 HELLO_FIND_REQUIRED ,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。
使用自定义的 Hello 与使用 curl 的方法一致。
设置动态库的版本号:
set_target_properties(${project_name} PROPERTIES VERSION 1.0.0 SOVERSION 1)
------