我现在项目的目录结构如下
project_root/
├── CMakeLists.txt
├── main.cpp 主函数
├── third_part/
│ ├── CMakeLists.txt # 总的 third_part CMakeLists.txt
│ ├── json/
│ │ ├── CMakeLists.txt # json 的 CMakeLists.txt
│ │ └── json.hpp
│ ├── spdlog/
│ │ ├── CMakeLists.txt # spdlog 的 CMakeLists.txt
│ │ ├── include/
│ │ │ └── spdlog/ # spdlog 的头文件
│ │ └── libspdlog.a # spdlog 静态库
│ ├── httplib/
│ │ ├── CMakeLists.txt # httplib 的 CMakeLists.txt
│ │ ├── httplib.cpp
│ │ └── httplib.h
│ └── inireader/
│ ├── CMakeLists.txt # INIReader 的 CMakeLists.txt
│ ├── ini.c
│ ├── ini.h
│ ├── INIReader.cpp
│ └── INIReader.h
├── basic/ # 业务基础代码
│ ├── CMakeLists.txt
│ ├── include/
│ ├── logic1.cpp
│ └── logicxxx.cpp
也即主函数,基础代码,第三方库。
第三方库有多种形式:
json :只有一个头文件
spdlog:有一个静态库和一个头文件目录
httplib:有一个头文件和源码文件
inireader:有两个头文件和两个源码文件
需要实现如下目标:
- 各个模块描述自己模块需要有哪些内容需要编译,哪些接口暴露出去。如果其他模块要使用本模块,只需用一行命令申明即可。编译时会自动链接这个模块,以及把模块的接口加入到头文件路径。
- third_part整个4个第三方库为一个库。其他模块只需要用一行命令导入third_part模块就能使用4个第三方库的功能。
下面介绍各个目录下的CMakeLists.txt
CMakeLists
顶层
cmake_minimum_required(VERSION 3.10) # cmake 版本最低为3.10
project(server) # 设置工程名为serverset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) #把动态库放在build目录下的lib目录。
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) #把静态库放在build目录下的lib目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) #把可执行文件放在build目录下的bin目录# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11) # 设置C++标准
add_compile_definitions(SPDLOG_COMPILED_LIB) # 添加编译器宏定义,即g++ -DSPDLOG_COMPILED_LIB,这个是spdlog要求的,这个库是日志库,哪里都用得到,所以在全局添加这个宏定义。# 读 子目录 third_part 和basic
add_subdirectory(third_part) #引入子目录third_part的内容
add_subdirectory(basic) #引入子目录basic的内容add_executable(server main.cpp) # 编译可执行文件,源文件只有一个erver.cpp,可执行文件名为server# 指定链接的库
target_link_libraries(server basic third_part mysqlcppconn) # 指明 可执行文件依赖的模块,有本工程的basic模块和third_part模块,还有系统提供的mysqlcppconn模块。这个就是目标2
顶层配置了整个项目的信息,所以看起来多,但其实去掉注释也不多。引用别的模块很简单,就一个target_link_libraries加模块名,就完成引用了。
third_part
add_subdirectory(json)
add_subdirectory(httplib)
add_subdirectory(INIReader)
add_subdirectory(spdlog)add_library(third_part INTERFACE)
target_link_libraries(third_part INTERFACE json httplib INIReader spdlog)
显示指出需要引入哪些子文件夹的内容。
然后申明本模块的名称,以及本模块整合了哪些模块。别的模块只需要target_link_librairies third_part模块就可以使用这些第三方库,无需操心头文件在哪里,这个第三方库以什么方式提供。
这个就是目标2的实现。
httplib
add_library(httplib STATIC httplib.cc)
target_include_directories(httplib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
httplib是本模块的模块名,这里和目录名同名,和目录名同名不是强制的。
STATIC 表示本模块将以一个静态库的形式呈现。对应的还有SHARED。以动态库形式呈现。
以及INTERFACE,以只头文件(header-only)形式呈现。
PUBLIC:当前模块和使用该模块的模块都会使用的头文件
对应的还有:
INTERFACE:当前模块不会使用的头文件
PRIVATE:只有当前模块会使用的头文件
申明本模块的名称,本模块涉及的源文件。以及本模块的头文件。PUBLIC意味着直接或者间接引用本模块的模块,都能找到本模块的头文件。
这个就是目标1的实现。他告诉了cmake本模块如何编译链接。以及本模块哪些内容是需要暴露给其他模块的。
INIReader
add_library(INIReader STATIC ini.c INIReader.cpp)
target_include_directories(INIReader PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
同httplib
Spdlog
这个模块是一个静态库,所以有不同
add_library(spdlog STATIC IMPORTED GLOBAL) # 申明为global才能被全局发现
set_target_properties(spdlog PROPERTIESIMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libspdlog.a) #指明静态库的位置
set_target_properties(spdlog PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include) # 告诉外界这个静态库的头文件,让外界能够发现这个模块的头文件。
IMPORTED 告诉cmake这个库是从外部引入的,不需要生成任何规则来构建这个库。直接使用现成的。spdlog是那个被导入的库在本项目的别名。此后,在本项目中,都用这个别名指代那个外部导入的库。
GLOBAL 默认情况下,IMPORTED的库只能在当前目录以及子目录可见,GLOBAL可以拓展可见范围为全局。
json
add_library(json INTERFACE)
target_include_directories(json INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
只有头文件,则用INTERFACE
INTERFACE:当前模块不会使用的头文件
PRIVATE:只有当前模块会使用的头文件
PUBLIC:当前模块和使用该模块的模块都会使用的头文件
basic
file(GLOB SOURCES "*.cpp")
add_library(basic STATIC ${SOURCES})
target_include_directories(basic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(basic third_part)
basic下的所有.cpp文件都是需要编译的,第一条指令可以把当前目录下所有的.cpp文件列出来放到SOURCES变量中。
GLOB用于以列出符合指定模式的文件。
GLOB_RECURSE类似,但是他会列出子目录的。