目录
spdlog项目构成
Github源码:https://github.com/gabime/spdlog
spdlog项目采用CMake构建,其一级目录结构如下
$ tree -L 1
.
├── CMakeLists.txt // 根目录CMake文件
├── INSTALL // 安装说明
├── LICENSE // license声明文件
├── README.md // 项目介绍文档
├── appveyor.yml // 用于自动化构建, Windows构建平台的配置
├── bench // benchmark, 用于综合测试
├── build // 用于存放编译过程产生的中间文件
├── cmake // 用于存放与cmake构建项目有关的文件
├── example // 用户例程
├── include // 头文件根目录
├── logos // 用于存放logo
├── scripts // 存放脚本文件
├── src // 源码目录
└── tests // 单元测试目录
本文主要分析项目构建、源码(src、include目录下),例程(example),单元测试(tests)。其他部分,必要时也解析。
CMake构建
分析一个CMake构建的项目的源码构成,不得不分析CMake文件。spdlog的CMake文件,从位置上讲,分为三部分:
1)根目录CMakeLists.txt,CMake命令入口,负责整个项目构建的主体配置;
2)cmake目录,包含多个cmake文件,定义了专门的CMake函数,适配IDE环境,.h输入文件;
3)源码子目录下的CMakeLists.txt文件,用于子目录下的目标构建;
因为3)通常比较简单,这里主要讲1)和2)。
根目录CMakeLists.txt
cmake版本要求
# 指明对cmake的最低、最高版本要求
# cmake_minimum_required(VERSION)调用了cmake_policy(VERSION)
cmake_minimum_required(VERSION 3.10...3.21)
include专用cmake文件
# ---------------------------------------------------------------------------------------
# Start spdlog project
# ---------------------------------------------------------------------------------------
include(cmake/utils.cmake)
include(cmake/ide.cmake)
spdlog_extract_version()
# 设置项目名、版本、语言
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
# 使用GNU标准安装目录
# CMake会根据CMAKE_INSTALL_PREFIX变量, 构建出绝对路径, 指明BINDIR、LIBDIR、INCLUDEDIR等各种路径
include(GNUInstallDirs)
include命令是直接将指定文件包含进当前cmake文件中。
utils.cmake文件包含一些工具函数,比如spdlog_extract_version,用于从.h文件中提取spdlog版本信息;
ide.cmake文件用于支持IDE,以相对路径方式将头文件目录定义为变量,这样其他cmake文件只需要引用变量即可;
spdlog通过GNUInstallDirs指明库使用GNU标准安装目录,即生成多个跟安装库有关的路径的变量,形如CMAKE_INSTALL_<dir>
,CMAKE_INSTALL_FULL_<dir>
,而在后续通过install指令将库安装到对应路径即可。
设置默认build类型
CMAKE_BUILD_TYPE默认值设为"Release"。还有另一个常用值"Debug"。
# ---------------------------------------------------------------------------------------
# Set default build to release
# ---------------------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE)
# cache 缓存条目, 可以通过CMake GUI的add按钮增加. 缓存条目本质上可以跨层级进行传递的变量, 类似于全局变量
# FORCE 默认情况下缓存条目的值不会被覆盖, 除非使用了选项FORCE
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif()
注意:set + CACHE + FORCE命令,可以让该值通过CMake GUI显示并修改。CMake GUI如下图所示:
编译器配置
spdlog支持多个编译器平台,在这部分指明。支持C++11,对于其他编译器如MSVC、CygWin,有额外配置。
# ---------------------------------------------------------------------------------------
# Compiler config
# ---------------------------------------------------------------------------------------
# CMAKE_CXX_STANDARD值为0
if(NOT CMAKE_CXX_STANDARD)
# 设置c++ 11 支持
set(CMAKE_CXX_STANDARD 11)
# 设置开启标准要求
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
# make sure __cplusplus is defined when using msvc and enable parallel build
if(MSVC)
# Win10 + MSVC, ${CMAKE_CXX_FLAGS}输出:
# /DWIN32 /D_WINDOWS /EHsc /Zc:__cplusplus /MP
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP")
endif()
# 关闭c++ 扩展
set(CMAKE_CXX_EXTENSIONS OFF)
# CYGWIN: Windows上运行的类UNIX模拟环境
# MSYS: Minimal GNU(POSIX)system on Windows,是一个小型的GNU环境. 类似于CYGWIN
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
set(CMAKE_CXX_EXTENSIONS ON)
endif()
判断当前项目是否为spdlog
用户可能以多种方式引入spdlog,可能是库,也可能是以源码形式。那么,构建时,CMake如何判断呢?
spdlog是通过当前cmake文件所在路径,是否为顶层路径,来判断的。因为如果是以头文件形式include,spdlog必然不会位于项目的根目录。而结果记录在自定义变量SPDLOG_MASTER_PROJECT。
# ---------------------------------------------------------------------------------------
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
# ---------------------------------------------------------------------------------------
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
if(NOT DEFINED SPDLOG_MASTER_PROJECT)
# CMAKE_CURRENT_SOURCE_DIR 当前正在处理的源目录的路径
# CMAKE_SOURCE_DIR 源树的顶层目录
# 也就是说, 目前没有定义SPDLOG_MASTER_PROJECT, 但当前CMakeLists位于顶层目录,
# 依然将SPDLOG_MASTER_PROJECT定义为ON(BOOL类型值 ON/OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
else()
set(SPDLOG_MASTER_PROJECT OFF)
endif()
endif()
选项开关
选项开关通过option来指定,用于控制编译流程。
AddressSanitizer是一个快速检测内存的工具,只能用于gcc/clang。如果想要使用,就需要配合打开一些编译选项。
关于AddressSanitizer更详细内容,可以参考这篇文章:AddressSanitizer使用介绍
# ---------------------------------------------------------------------------------------
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
# ---------------------------------------------------------------------------------------
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
if(NOT DEFINED SPDLOG_MASTER_PROJECT)
# CMAKE_CURRENT_SOURCE_DIR 当前正在处理的源目录的路径
# CMAKE_SOURCE_DIR 源树的顶层目录
# 也就是说, 目前没有定义SPDLOG_MASTER_PROJECT, 但当前CMakeLists位于顶层目录,
# 依然将SPDLOG_MASTER_PROJECT定义为ON(BOOL类型值 ON/OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
else()
set(SPDLOG_MASTER_PROJECT OFF)
endif()
endif()
# option 用于控制编译流程
# option格式: option(<variable> "<help_text>" [value])
# value定义选项默认状态, 值一般是OFF或ON, 除去ON, 其他所有值默认OFF
# 该选项为ON, 代表打开所有SPDLOG_BUILD_XXX选项都打开
# SPDLOG_BUILD_ALL 控制是否编译所有目标, 如example, tests, bench
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
# SPDLOG_BUILD_SHARED 控制是否编译为静态库(.lib/.a)
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
# precompiled headers option 控制是否预编译头文件, 能加快编译速度; 但如果文件有修改, 就会需要重新编译.
option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF)
# build position independent code
option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF)
# FIXME: 默认值OFF?
# example options
# SPDLOG_BUILD_EXAMPLE 控制是否编译example目录下的例程
option(SPDLOG_BUILD_EXAMPLE "Build exmpale" ${SPDLOG_MASTER_PROJECT})
# SPDLOG_BUILD_EXAMPLE_HO 控制是否以头文件形式, 编译exmample目录下的例程
option(SPDLOG_BUILD_EXAMPLE_HO "Build head only exmaple" OFF)
# testing options
# 控制是否编译test目录下的例程
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
option(SPDLOG_BUILD_TESTS_HO "Build header only example" OFF)
# 需要安装google benchmark 测试工具 (需要安装google benckmark)
# bench options
# 控制释放编译bench目录下的例程
option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
# AddressSanitizer 是一个快速的内存错误检测工具 (需要安装: https://github.com/google/sanitizers/wiki/AddressSanitizer)
# sanitizer options 控制是否使用AddressSanitizer
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
# warning options 控制编译警告信息
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
# install options 控制是否安装库. 当以外部链接库方式引入时, 必须打开此选项
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
# SPDLOG_USE_STD_FORMAT 控制是否使用std::format
# std::format 要求C++20, 而且只有部分编译器实现, 查看: https://en.cppreference.com/w/cpp/compiler_support
option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library. No compile-time format string checking." OFF)
# SPDLOG_FMT_EXTERNAL 控制使用外部fmt库取代spdlog内置的
# fmt库官网: https://fmt.dev/latest/index.html
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
# SPDLOG_FMT_EXTERNAL_HO 控制使用外部fmt库(头文件方式)取代spdlog内置的
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
# SPDLOG_NO_EXCEPTIONS 遇到异常时, 直接调用abort()终止进程, 也就是说不抛出异常
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
# 不允许同时开启的2个选项, 因为是互斥的
if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
# FATAL_ERROR: 产生 CMake Error,会停止编译系统的构建过程
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
endif()
# std::format跟外部fmt库不能同时开启
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO)
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
endif()
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL)
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive")
endif()
# misc tweakme options
if (WIN32) # Windows平台
# 宽字符支持选项
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
else()
set(SPDLOG_WCHAR_SUPPORT OFF CACHE BOOL "non supported option" FORCE)
set(SPDLOG_WCHAR_FILENAMES OFF CACHE BOOL "non supported option" FORCE)
endif ()
# 检查操作系统名称, 如果是Linux平台, 就开启SPDLOG_CLOCK_COARSE选项
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# 指定CLOCK_REALTIME_COARSE选项后, clock_gettime() 粗粒度获取时间, 精度由ns变为ms
# 另一个选项CLOCK_MONOTONIC_COARSE
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
else()
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
endif ()
# 阻止子进程继承log file的文件描述符. 如果是类Unix系统, 打开文件时, 指定O_CLOEXEC选项
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
# 阻止查询thread id. 如果是Linux系统, 通过系统调用__NR_gettid来读取thread id
option(SPDLOG_NO_THREAD_ID "prevent spdlog from query the thread id on each log call if thread id is not needed" OFF)
# 阻止thread local存储
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
# 阻止以atomic<T>存储 log level
option(SPDLOG_NO_ATOMIC_LEVELS
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
OFF)
# 关闭default logger, default logger在程序中属于registry class 数据成员
option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
# clang-tidy是一个静态代码分析框架
if (${CMAKE_VERSION} VERSION_GREATER "3.5")
option(SPDLOG_TIDY "run clang-tidy" OFF)
endif ()
if (SPDLOG_TIDY)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
# 开启后,其生成的文件compile_commands.json,包含所有编译单元所执行的指令
# 在生成期间, 决定是否启用编译命令输出
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
message(STATUS "Enabled clang-tidy")
endif ()
if (SPDLOG_BUILD_PIC)
# 该值是POSITION_INDEPENDENT_CODE的默认值, 用于初始化所有目标上的POSITION_INDEPENDENT_CODE属性
# POSITION_INDEPENDENT_CODE是否创建与位置无关的目标, 打开后生成编译选项 -fPIC
# https://blog.csdn.net/zhizhengguan/article/details/115323750
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif ()
# 搜索Threads库, 要求必须存在
find_package(Threads REQUIRED)
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
标签:OFF,option,源码,spdlog,CMAKE,CMake,cmake,SPDLOG
From: https://www.cnblogs.com/fortunely/p/17559174.html