本章节通过一个工程介绍下cmake工程各个模块。使用JetBrains Clion开发工具组织代码。
https://github.com/jasbin2008/cmake-learn.git
1. 多个源文件组织
创建一个工程,添加以下文件:
操作步骤:
1)在根CMakeLists.txt中配置所有子目录下的源文件
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
PROJECT(PROJECT_ONE)
add_executable(main main.cpp mod1/mod1.cpp mod1/mod1_func.cpp) # 指明需要的源代码文件就好
2)在main.cpp中添加mod1.h,直接调用
2. 使用动态库
现在以动态库的形式重新构建mod1:
1)在mod1文件夹中创建CMakeLists.txt,用于创建动态库mod1
# ./mod1/CMakeLists.txt
add_library(mod1 SHARED mod1.cpp mod1_func.cpp)
2)在根目录下的CMakeLists.txt中配置mod1
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
PROJECT(PROJECT_ONE)
add_subdirectory(mod1 lib) # 添加一个模块,并且将编译好库文件放置在 build/lib 目录
add_executable(main main.cpp)
target_link_libraries(main mod1) # 链接 mod1
3)在main.cpp中添加mod1.h,调用动态库mo1
注意:如果在Windows下使用开发工具clion,生成的动态库是dll,直接调用会出错误,建议在Linux平台下运行
3. 使用静态库
1)在mod2文件夹中新增CMakeLists.txt,用于生成静态库mod2
# ./mod1/mod2/CMakeLists.txt
add_library(mod2 STATIC mod2.cpp)
2)在mod1文件夹中修改CMakeLists.txt,配置静态库mod2
# ./mod1/CMakeLists.txt
add_subdirectory(mod2 mo2_lib) #新增 mod2 模块, 编译好的库置于 build/lib/mod2_lib 中
link_directories(mod2_lib) #添加链接器的查找路径 build/lib/mod2_lib
add_library(mod1 SHARED mod1.cpp mod1_func.cpp)
target_link_libraries(mod1 mod2)
3)在mod1_func.cpp中添加mo2.h,调用静态库mod2
4. 安装程序
各个目录的CMakeLists.txt各自负责自己目录下要安装的文件:
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
PROJECT(PROJECT_ONE)
add_subdirectory(mod1 lib) # 添加一个模块,并且将编译好库文件放置在 build/lib 目录
add_executable(main main.c)
target_link_libraries(main mod1) # 链接 mod1
set(CMAKE_INSTALL_PREFIX $ENV{HOME}/usr) # 安装路径前缀 /home/cao/usr
install(DIRECTORY doc/ DESTINATION share/PROJECT_ONE) # 安装项目文档
install(TARGETS main RUNTIME DESTINATION bin ) # main 安装到 usr/bin
# ./mod1/CMakeLists.txt
add_subdirectory(mod2 mo2_lib) # 新增 mod2 模块, 编译好的库置于 build/lib/mod2_lib 中
link_directories(mod2_lib) # 添加链接器的查找路径 build/lib/mod2_lib
add_library(mod1 SHARED mod1.c mod1_func.c) # 生成动态库 libmod1.so
target_link_libraries(mod1 mod2) # 将 libmod2.a 链接进入 libmod1.so 中
install(TARGETS mod1 ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) # 安装到 usr/lib
install(FILES mod1.h DESTINATION include/mod1) # 安装到 usr/include/mod1
# ./mod1/mod2/CMakeLists.txt
add_library(mod2 SHARED mod2.c) # 生成静态库 libmod2.a
install(TARGETS mod2 ARCHIVE DESTINATION LIBRARY DESTINATION lib) # 安装到 usr/lib
install(FILES mod2.h DESTINATION include/mod2) # 安装到 usr/include/mod2
5. 使用Find模块
1)添加Find模块,命名符合Find<name>.cmake
规范,这里以添加libxml2库为例:
FINDLIBXML2.cmake
find_path(LIBXML2_INCLUDE_DIR xmlmemory.h /usr/local/include/libxml2/libxml)
find_library(LIBXML2_LIBRARY NAMES libxml2.so PATH /usr/local/lib)
if(LIBXML2_INCLUDE_DIR AND LIBXML2_LIBRARY)
set(LIBXML2_FOUND TRUE)
endif(LIBXML2_INCLUDE_DIR AND LIBXML2_LIBRARY)
if (LIBXML2_FOUND)
if(NOT LIBXML2_FOUND_QUIETLY)
message(STATUS "Found Hello: ${LIBXML2_LIBRARY}")
endif(NOT LIBXML2_FOUND_QUIETLY)
else(LIBXML2_FOUND)
if(LIBXML2_FOUND_QUIETLY)
message(FATAL_ERROR "Could not find hello library")
endif(LIBXML2_FOUND_QUIETLY)
endif(LIBXML2_FOUND)
2)修改根CMakeLists.txt,添加模块查找代码
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) # find_package会在CMAKE_MODULE_PATH查找
# 连接 libxml2.lib
find_package(LIBXML2)
if(LIBXML2_FOUND)
include_directories(${LIBXML2_INCLUDE_DIR})
include_directories(/usr/local/include/libxml2)
link_directories(/usr/local/lib)
target_link_libraries(main xml2)
else(LIBXML2_FOUND)
message(FATAL_ERROR "libxml2.so not be found!")
endif(LIBXML2_FOUND)
3)在main.cpp中使用库
// main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mod1/mod1.h"
#include "xmlmemory.h"
#include "parser.h"
const char *curDoc = "<?xml version=\"1.0\"?>"
"<story>"
"<storyinfo>"
"<author>John Fleck</author>"
"<datewritten>June 2, 2002</datewritten>"
"<keyword>example keyword</keyword>"
"</storyinfo>"
"<body>"
"<headline>This is the headline</headline>"
"<para>This is the body text.</para>"
"</body>"
"</story>";
int main( int argc, char *argv[] )
{
xmlDocPtr doc;
xmlNodePtr cur;
//doc = xmlParseFile(docname);
doc = xmlParseDoc((const xmlChar *)curDoc);
if (doc == NULL) {
fprintf(stderr, "Document not parsed successfully. \n");
return -1;
}
printf("parse document success!\n");
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr, "empty document\n");
xmlFreeDoc(doc);
return -1;
}
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr, "document of the wrong type, root node != story");
xmlFreeDoc(doc);
return -1;
}
printf("root elment value is %s\n", cur->name);
xmlFreeDoc(doc);
return 0;
}
6. 参数控制
在CMakeLists.txt中配置参数,控制源代码中代码的编译部分,比如可以通过一个参数控制,是用自己写的库还是系统库?
1)添加cmakeconfig.h.in
#define AUTHOR "@AUTHOR@"
#define RELEASE_DATE "@RELEASE_DATE@"
#define USE_MY_LIB @USE_MY_LIB@
2)在根CMakeList.txt中配置,并包含生成的cmakeconfig.h目录
# 通过cmakeconfig.h传递参数给源文件
set(AUTHOR "user")
set(RELEASE_DATE "2019-11-06")
set(USE_MY_LIB "0")
configure_file(
${PROJECT_SOURCE_DIR}/cmakeconfig.h.in
${PROJECT_BINARY_DIR}/cmakeconfig.h
)
include_directories(${PROJECT_BINARY_DIR})
3)在main中使用
#ifdef USE_MY_LIB
printf("use my library\n");
#else
printf("use %s library\n", AUTHOR)
#endif
7. 调试版本和发布版本
上述的代码编译后都是不可调试的,并且没有做编译优化,我们希望能够编译成一个调试版本与一个发布版本。做法如下:
我们将build目录作为开发版本编译目录,与之相对的新建一个release目录作为发布版本
在build
目录下我们执行cmake -DMAKE_BUILD_TYPE=Debug ..
,编译命令会使用-g
在release
目录下我们执行cmake -DMAKE_BUILD_TYPE=Release ..
,编译命令会使用-O3 -DNDEBUG
所以,在源代码中,我们可以使用NDEBUG宏来控制,在开发版输出调试信息,而在发布版本去掉调试信息。
1)修改CMakeLists.txt,添加开发版本的编译参数
# 设置debug和release调试信息
set(CMAKE_C_FLAGS_DEBUG "-g -Wall -pedantic -DDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -pedantic -DDEBUG")
message(STATUS "debug flags: ${CMAKE_C_FLAGS_DEBUG}")
message(STATUS "release flags: ${CMAKE_C_FLAGS_RELEASE}")
2)在main.cpp中使用
#ifndef NDEBUG
printf("author: %s, release_date: %s\n", AUTHOR, RELEASE_DATE); //只在开发版本编译
#endif
3)添加sh直接编译
开发调试脚本
#!/bin/bash
rm -rf build/* # 清理上一次的结果
cd build && cmake -DCMAKE_BUILD_TYPE=debug .. # 进入debug目录,执行构建
make && ./main # 编译,然后运行
开发发布版本
#!/bin/bash
if [ ! -d ./release ]
then
mkdir release
else
rm -rf release/*
fi
cd release && cmake -DCMAKE_BUILD_TYPE=release ..
make && ./main
.. # 进入debug目录,执行构建
make && ./main # 编译,然后运行
来源:CSDN
作者:JasBin2008
链接:https://blog.csdn.net/JasBin2008/article/details/104192786