• 基于CMake的C++项目管理实践



    基于CMake的C++项目管理实践

    使用CMake的find_package和configuration文件进行管理

    以一个例子作为说明:

    • liba: lib库
    • libb: lib库,且引用liba
    • tutorial: exe,且引用liba和libb

    示例代码项目结构

    liba库-项目结构:

    liba
    ├── build_debug/ : Debug模式构建目录
    ├── build_release/ : Release模式构建目录
    ├── call/ : call.lib 库目录
    │   ├── CMakeLists.txt
    │   ├── usage_func.cxx
    │   └── usage_func.hpp
    ├── CMakeLists.txt
    ├── Config.cmake.in
    ├── inherit/ : inherit.lib 库目录
    │   ├── base_func.cxx
    │   ├── base_func.hpp
    │   ├── CMakeLists.txt
    │   ├── mysqrt.cxx
    │   └── mysqrt.h
    ├── liba_config.hpp.in
    └── liba_exports.hpp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    libb库-项目结构:

    libb
    ├── build_debug/ : Debug模式构建目录
    ├── build_release/ : Release模式构建目录
    ├── CMakeLists.txt
    ├── Config.cmake.in
    ├── libb_config.hpp.in
    ├── libb_exports.hpp
    └── redefining/ : redefining.lib 库目录
        ├── CMakeLists.txt
        ├── derived_func.cxx
        └── derived_func.hpp
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    tutorial库-项目结构:

    liba
    ├── build_debug/ : Debug模式构建目录
    ├── build_release/ : Release模式构建目录
    ├── CMakeLists.txt
    └── main.cxx
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例代码文件内容

    liba库-内容

    call/usage_func.hpp:
    #pragma once 
    
    #include "liba_exports.hpp"
    
    namespace liba{
        class LIBA_DECLSPEC CUsageFunction {
        public:
            CUsageFunction() = default;
            virtual~CUsageFunction() = default;
    
            double sqrt(double);
    
            void message();
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    call/usage_func.cxx:
    #include "usage_func.hpp"
    
    #include 
    #include 
    
    namespace liba {
        double CUsageFunction::sqrt(double num) {
    
            double v = std::sqrt(num);
            std::cout << "liba CUsageFunction sqrt on cmath: std::sqrt(" << num << ")=" << v << std::endl;  
            return v;
        }
    
        void CUsageFunction::message() {
            std::cout << "liba CUsageFunction message!" << std::endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    call/CMakeLists.txt:
    set(COMPONENT call)
    
    set(SRC_LST usage_func.cxx)
    
    # 设置-DBUILD_SHARED_LIBS=ON后,此处可以不显式设置类型(STATIC, SHARED),也可以生成具体类型的库
    add_library(${COMPONENT} ${SRC_LST})
    
    # namespaced alias
    add_library(${CMAKE_PROJECT_NAME}::${COMPONENT} ALIAS ${COMPONENT})
    
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "${COMPONENT}-CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_BINARY_DIR:" ${PROJECT_BINARY_DIR})
    
    # state that anybody linking to us needs to include the current source dir
    # to find base_func.hpp, while we don't.
    # 调用程序链接inherit本目标时,需要包含base_func.hpp
    # 但是inherit本目标编译时,不需要链接base_func.hpp
    # 利用INTERFACE作用域限定include作用域:target本身不链接,传递到调用方链接
    # ${CMAKE_CURRENT_SOURCE_DIR}: The path to the source directory currently being processed. 
    # ${CMAKE_CURRENT_SOURCE_DIR}: 当前处理的源目录,即当前CMakeLists.txt所在目录
    # ${PROJECT_SOURCE_DIR}: 表示项目主CMakeLists.txt文件所在目录
    # ${CMAKE_INSTALL_INCLUDEDIR}: include
    # $: 表示库链接过程中需要包含的路径
    # $: 表示在安装目录下引用库需要包含的路径
    # 项目链接了${PROJECT_SOURCE_DIR}/liba_exports.hpp 故${PROJECT_SOURCE_DIR}作用域需要设置为PUBLIC
    target_include_directories(${COMPONENT}
                               INTERFACE
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                               $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
                               $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
                               PUBLIC
                               $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
                               )
    
    # 添加BUILD_SHARED_LIBS判断是否编译动态库和静态库
    if(BUILD_SHARED_LIBS)
      # 在Windows平台下,动态库和静态库的导出符号需要区别对待
      target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBA_BUILD")
    endif()
    
    # link our compiler flags interface library
    # project_cxx_compiler_flags: 项目主CMakeLists.txt文件中定义的继承了C++特性的接口库
    # 链接接口库的编译标记
    # target_link_libraries(${COMPONENT} PUBLIC project_cxx_compiler_flags)
    target_link_libraries(${COMPONENT} PRIVATE project_cxx_compiler_flags)
    
    # 定义windows通用的导入导出符号,否则windows平台不会为dll生成导入库lib
    target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBA_EXPORTING")
    
    # 为target添加DEBUG_POSTFIX属性
    set_target_properties(${COMPONENT} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
    
    # 安装lib
    # set(installable_libs ${COMPONENT} project_cxx_compiler_flags)
    set(installable_libs ${COMPONENT})
    # 若需要提供SqrtLibrary安装,则执行如下代码
    # if(TARGET SqrtLibrary)
    #   # 将SqrtLibrary添加到installable_libs 变量中
    #   list(APPEND installable_libs SqrtLibrary)
    # endif()
    
    # EXPORT可以导出其他CMake项目直接使用此项目的信息
    # 导出后可以将信息输出到xxx.cmake文件,方便其它项目find_package调用
    install(TARGETS ${installable_libs}
            EXPORT ${COMPONENT}-Targets
            LIBRARY DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            ARCHIVE DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            RUNTIME DESTINATION $<IF:$<CONFIG:Debug>,bind,bin>
            INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
            
    # 安装include
    # 方法一:
    # install(FILES usage_func.hpp DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
    # 方法二:
    install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
            FILES_MATCHING PATTERN "*.hpp")
    
    # 生成和安装export file
    # generate and install export file
    install(EXPORT ${COMPONENT}-Targets
            FILE ${CMAKE_PROJECT_NAME}-${COMPONENT}Targets.cmake
            NAMESPACE ${CMAKE_PROJECT_NAME}::
            DESTINATION "cmake")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    inherit/base_func.hpp:
    #pragma once 
    
    #include "liba_exports.hpp"
    
    namespace liba{
        class LIBA_DECLSPEC CBaseFunction {
        public:
            CBaseFunction() = default;
            virtual~CBaseFunction() = default;
    
            double sqrt(double);
    
            void message();
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    inherit/base_func.cxx:
    #include "base_func.hpp"
    
    #include 
    
    #ifdef USE_MYMATH
    #   include "mysqrt.h"
    #else 
    #   include 
    #endif
    
    namespace liba {
        double CBaseFunction::sqrt(double num) {
    #ifdef USE_MYMATH
            double v = detail::mysqrt(num);
            std::cout << "liba CBaseFunction sqrt on mysqrt: mysqrt(" << num << ")=" << v << std::endl;  
            return v;
    #else
            double v = std::sqrt(num);
            std::cout << "liba CBaseFunction sqrt on cmath: std::sqrt(" << num << ")=" << v << std::endl;  
            return v;
    #endif
        }
    
        void CBaseFunction::message() {
            std::cout << "liba CBaseFunction message!" << std::endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    inherit/CMakeLists.txt:
    set(COMPONENT inherit)
    
    set(SRC_LST base_func.cxx)
    
    # 设置-DBUILD_SHARED_LIBS=ON后,此处可以不显式设置类型(STATIC, SHARED),也可以生成具体类型的库
    add_library(${COMPONENT} ${SRC_LST})
    
    # namespaced alias
    add_library(${CMAKE_PROJECT_NAME}::${COMPONENT} ALIAS ${COMPONENT})
    
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "${COMPONENT}-CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_BINARY_DIR:" ${PROJECT_BINARY_DIR})
    
    # state that anybody linking to us needs to include the current source dir
    # to find base_func.hpp, while we don't.
    # 调用程序链接inherit本目标时,需要包含base_func.hpp
    # 但是inherit本目标编译时,不需要链接base_func.hpp
    # 利用INTERFACE作用域限定include作用域:target本身不链接,传递到调用方链接
    # ${CMAKE_CURRENT_SOURCE_DIR}: The path to the source directory currently being processed. 
    # ${CMAKE_CURRENT_SOURCE_DIR}: 当前处理的源目录,即当前CMakeLists.txt所在目录
    # ${PROJECT_SOURCE_DIR}: 表示项目主CMakeLists.txt文件所在目录
    # ${CMAKE_INSTALL_INCLUDEDIR}: include
    # $: 表示库链接过程中需要包含的路径
    # $: 表示在安装目录下引用库需要包含的路径
    # 项目链接了${PROJECT_SOURCE_DIR}/liba_exports.hpp 故${PROJECT_SOURCE_DIR}作用域需要设置为PUBLIC
    target_include_directories(${COMPONENT}
                               INTERFACE
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                               $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
                               $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
                               PUBLIC
                               $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
                               )
    
    # should we use our own math functions
    # option定义CMake构建编译系统的选项
    option(USE_MYMATH "Use mymath implementation" ON)
    
    if(USE_MYMATH)
      # target_compile_definitions定义CMake编译选项
      # 如下面语句为C++源码定义了USE_MYMATH宏定义
      # PRIVATE: 作用域只限于本项目
      target_compile_definitions(${COMPONENT} PRIVATE "USE_MYMATH")
    
      # USE_MYMATH模式下 使用项目自定义sqrt实现
      # 定义SqrtLibrary静态库
      add_library(SqrtLibrary STATIC mysqrt.cxx)
    
      # state that SqrtLibrary need PIC when the default is shared libraries
      # 当inherit构建动态库时,SqrtLibrary静态库的POSITION_INDEPENDENT_CODE属性不设置为True,构建会失败
      set_target_properties(SqrtLibrary PROPERTIES
                            POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                            )
      
      # link our compiler flags interface library
      # project_cxx_compiler_flags: 项目主CMakeLists.txt文件中定义的继承了C++特性的接口库
      # 链接接口库的编译标记
      target_link_libraries(SqrtLibrary PUBLIC project_cxx_compiler_flags)
    
      # 为inherit目标添加链接库SqrtLibrary
      # PRIVATE:SqrtLibrary作用域仅限于本项目
      target_link_libraries(${COMPONENT} PRIVATE SqrtLibrary)
    endif()
    
    # 添加BUILD_SHARED_LIBS判断是否编译动态库和静态库
    if(BUILD_SHARED_LIBS)
      # 在Windows平台下,动态库和静态库的导出符号需要区别对待
      target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBA_BUILD")
    endif()
    
    # link our compiler flags interface library
    # project_cxx_compiler_flags: 项目主CMakeLists.txt文件中定义的继承了C++特性的接口库
    # 链接接口库的编译标记
    # target_link_libraries(${COMPONENT} PUBLIC project_cxx_compiler_flags)
    target_link_libraries(${COMPONENT} PRIVATE project_cxx_compiler_flags)
    
    # 定义windows通用的导入导出符号,否则windows平台不会为dll生成导入库lib
    target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBA_EXPORTING")
    
    # 为target添加DEBUG_POSTFIX属性
    set_target_properties(${COMPONENT} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
    
    # 安装lib
    # set(installable_libs ${COMPONENT} project_cxx_compiler_flags)
    set(installable_libs ${COMPONENT})
    # 若需要提供SqrtLibrary安装,则执行如下代码
    # if(TARGET SqrtLibrary)
    #   # 将SqrtLibrary添加到installable_libs 变量中
    #   list(APPEND installable_libs SqrtLibrary)
    # endif()
    
    # EXPORT可以导出其他CMake项目直接使用此项目的信息
    # 导出后可以将信息输出到xxx.cmake文件,方便其它项目find_package调用
    install(TARGETS ${installable_libs}
            EXPORT ${COMPONENT}-Targets
            LIBRARY DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            ARCHIVE DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            RUNTIME DESTINATION $<IF:$<CONFIG:Debug>,bind,bin>
            INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
            
    # 安装include
    # 方法一:
    # install(FILES base_func.hpp DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
    # 方法二:
    install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
            FILES_MATCHING PATTERN "*.hpp")
    
    # 生成和安装export file
    # generate and install export file
    install(EXPORT ${COMPONENT}-Targets
            FILE ${CMAKE_PROJECT_NAME}-${COMPONENT}Targets.cmake
            NAMESPACE ${CMAKE_PROJECT_NAME}::
            DESTINATION "cmake")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    liba_exports.hpp:
    #pragma once 
    
    #if (defined(_WIN32) || defined(_WIN64))
    #  if defined(SHARED_LIBA_BUILD)
    #    if defined(SHARED_LIBA_EXPORTING)
    #      define LIBA_DECLSPEC __declspec(dllexport)
    #    else
    #      define LIBA_DECLSPEC __declspec(dllimport)
    #    endif
    #  else // non shared
    #    define LIBA_DECLSPEC
    #  endif
    #else // non windows
    #  define LIBA_DECLSPEC
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    liba_config.hpp.in:
    // the configured options and settings for liba
    // 
    //# ${LIBA_VERSION_MAJOR} = ${PROJECT_VERSION_MAJOR}
    //# ${LIBA_VERSION_MINOR} = ${PROJECT_VERSION_MINOR}
    
    #pragma once
    
    #define LIBA_PROJ_VERSION "@PROJECT_VERSION@"
    #define LIBA_PROJ_VERSION_MAJOR @LIBA_VERSION_MAJOR@
    #define LIBA_PROJ_VERSION_MINOR @PROJECT_VERSION_MINOR@
    #define LIBA_PROJ_VERSION_PATCH @LIBA_VERSION_PATCH@
    #define LIBA_PROJ_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
    #define TEST_VERBOSE "@TEST_VERBOSE_FLAG@"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Config.cmake.in:
    @PACKAGE_INIT@
    
    # include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
    
    # _FOUND=False
    # __FOUND=True
    
    set(_liba_supported_components inherit call)
    
    foreach(_comp ${LIBA_FIND_COMPONENTS})
      if (NOT _comp IN_LIST _liba_supported_components)
        set(LIBA_FOUND False)
        set(LIBA_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
      endif()
      include("${CMAKE_CURRENT_LIST_DIR}/LIBA-${_comp}Targets.cmake")
    endforeach()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    CMakeLists.txt:
    cmake_minimum_required(VERSION 3.20)
    
    # project指定项目名、版本号、编程语言
    # 关于软件版本命名规则:主版本号.子版本号.修订版本号.日期-阶段版本号
    # * 主版本号:当功能模块有较大的变动,比如增加多个模块或者整体架构发生变化。此版本号由项目决定是否修改。
    # * 子版本号:当功能有一定的增加或变化,比如增加了对权限控制、增加自定义视图等功能。此版本号由项目决定是否修改
    # * 修订版本号:一般是 Bug 修复或是一些小的变动,要经常发布修订版,时间间隔不限,修复一个严重的bug即可发布一个修订版。此版本号由项目经理决定是否修改。
    # * 日期-阶段版本号:日期用于记录修改项目的当前日期,此版本号由开发人员决定是否修改;阶段版本号用于标注当前版本的软件处于哪个开发阶段,此版本号由项目决定是否修改,一般是希腊字母。
    # 例如,1.1.12.20230117-Release
    # 阶段版本号说明:
    # * Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。
    # * Alpha:此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。
    # * Beta: 该版本相对于Alpha版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过多次测试来进一步消除。
    # * RC:该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。
    # * Release:该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。
    # 关于CMake的版本规则:MAJOR.MINOR.PATCH.TWEAK
    # CMake的版本号各个部分必须是数字组成
    # 例如:1.1.1.20231017
    project(LIBA VERSION 1.1.1.20231017 LANGUAGES C CXX)
    
    # make cache variables for install destinations
    include(GNUInstallDirs)
    
    # 指定C++编译标准
    # 方式一:利用set指定 the C++ standard
    # set(CMAKE_CXX_STANDARD 11)
    # set(CMAKE_CXX_STANDARD_REQUIRED True)
    # 方式二:利用接口库的形式
    add_library(project_cxx_compiler_flags INTERFACE)
    target_compile_features(project_cxx_compiler_flags INTERFACE cxx_std_11)
    
    # debug模式以'd'结尾
    set(CMAKE_DEBUG_POSTFIX d)
    
    # 指定自定义变量
    set(TEST_VERBOSE_FLAG "${PROJECT_NAME} Demo")
    
    # 判断当前使用的编译器
    # 基于生成器表达式
    # $: 当项目所用编程语言匹配language,
    # 且项目所用编译器匹配compiler_ids时,此表达式返回1,否则返回0
    # * Create a new variable gcc_like_cxx that is true if we are using CXX and
    #   any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
    # * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
    set(gcc_like_cxx "$")
    set(msvc_cxx "$")
    
    # 为接口库project_cxx_compiler_flags 增加编译警告选项
    # 基于条件生成器表达式
    # * $ : 当condition为真则返回true_string,否则返回空字符串
    # * $ : 当condition为真则返回true_string,否则返回false_string
    # 生成器表达式支持嵌套
    # INTERFACE作用域表示project_cxx_compiler_flags 本身不需要,但是project_cxx_compiler_flags 调用方需要使用
    # BUILD_INTERFACE生成器表达式
    # 我们只想在编译阶段使用警告选项,而不会在安装阶段使用,可以利用BUILD_INTERFACE限制
    target_compile_options(project_cxx_compiler_flags INTERFACE 
      "$<${gcc_like_cxx}:$>"
      "$<${msvc_cxx}:$>"
    )
    
    # BUILD_SHARED_LIBS控制库类型的生成
    option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
    
    # configure a header file to pass some of the CMake settings
    # to the source code
    # 利用configure_file()命令可以实现文件复制,并且替换文件中@var@的变量值
    configure_file(liba_config.hpp.in ${PROJECT_BINARY_DIR}/include/liba_config.hpp)
    
    message(STATUS "PROJECT_NAME:" ${PROJECT_NAME})
    message(STATUS "CMAKE_PROJECT_NAME:" ${CMAKE_PROJECT_NAME})
    message(STATUS "CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "CMAKE_CURRENT_BINARY_DIR:" ${CMAKE_CURRENT_BINARY_DIR})
    
    message(STATUS "LIBA_BINARY_DIR:" ${LIBA_BINARY_DIR})
    message(STATUS "PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "CMAKE_SOURCE_DIR:" ${CMAKE_SOURCE_DIR})
    
    # 配置默认安装路径
    if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
      # ${CMAKE_SOURCE_DIR}表示源码树顶层目录路径
      set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/../install/${PROJECT_NAME})
    endif()
    
    # 添加inherit库和call库
    add_subdirectory(${PROJECT_SOURCE_DIR}/inherit inherit_build)
    add_subdirectory(${PROJECT_SOURCE_DIR}/call call_build)
    
    # 安装config.hpp exports.hpp
    install(FILES "${PROJECT_BINARY_DIR}/include/liba_config.hpp" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    install(FILES "${PROJECT_SOURCE_DIR}/liba_exports.hpp" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    
    # include CMakePackageConfigHelpers macro
    include(CMakePackageConfigHelpers)
    
    # 针对target设置版本属性
    #set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${PROJECT_VERSION})
    #set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR})
    #set_property(TARGET ${PROJECT_NAME} PROPERTY
    #  INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
    #set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY
    #  COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION
    #)
    
    # generate the version file for the config file
    write_basic_package_version_file(
      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
      VERSION "${PROJECT_VERSION}"
      COMPATIBILITY AnyNewerVersion
    )
    # generate the config file that includes the exports
    configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
      INSTALL_DESTINATION "cmake"
      NO_SET_AND_CHECK_MACRO
      NO_CHECK_REQUIRED_COMPONENTS_MACRO
    )
    # install config files
    install(FILES
      ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
      ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
      DESTINATION "cmake"
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    libb库-内容

    redefining/derived_func.hpp:
    #pragma once
    
    #include "libb_exports.hpp"
    
    #include 
    
    namespace libb {
        class LIBB_DECLSPEC CDerivedFunction : public liba::CBaseFunction {
        public:
            CDerivedFunction() = default;
            virtual~CDerivedFunction() = default;
    
            // 重定义(redefining)
            double sqrt(double);
    
            void message();
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    redefining/derived_func.cxx:
    #include "derived_func.hpp"
    
    #include 
    #include 
    
    namespace libb {
        double CDerivedFunction::sqrt(double num) {
    
            double v = std::sqrt(num);
            std::cout << "liba CDerivedFunction sqrt on cmath: std::sqrt(" << num << ")=" << v << std::endl;
            return v;  
        }
    
        void CDerivedFunction::message() {
            std::cout << "liba CDerivedFunction message!" << std::endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    redefining/CMakeLists.txt:
    set(COMPONENT redefining)
    
    set(SRC_LST derived_func.cxx)
    
    message(STATUS "${COMPONENT}-find_package location(CMAKE_CURRENT_SOURCE_DIR):" "${CMAKE_CURRENT_SOURCE_DIR}/../install/LIBA/cmake")
    message(STATUS "${COMPONENT}-find_package location(CMAKE_SOURCE_DIR):" "${CMAKE_SOURCE_DIR}/../install/LIBA/cmake")
    
    # 添加路径到CMAKE_PREFIX_PATH 使find_package可以搜索加载package
    list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/../install/LIBA/cmake")
    
    foreach(_dir ${CMAKE_PREFIX_PATH})
      message(STATUS "${CMAKE_PROJECT_NAME}::${COMPONENT}-find_package in dir:${_dir}")
    endforeach()
    
    find_package(LIBA 1.1.0 REQUIRED COMPONENTS inherit)
    if(NOT LIBA_FOUND)
      message(FATAL_ERROR "${COMPONENT}-package: LIBA not found!")
    endif()
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # 设置-DBUILD_SHARED_LIBS=ON后,此处可以不显式设置类型(STATIC, SHARED),也可以生成具体类型的库
    # add_library(${COMPONENT} ${SRC_LST} ${project_cxx_compiler_flags})
    add_library(${COMPONENT} ${SRC_LST})
    
    # namespaced alias
    add_library(${CMAKE_PROJECT_NAME}::${COMPONENT} ALIAS ${COMPONENT})
    
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "Current COMPONENT:" ${COMPONENT})
    message(STATUS "${COMPONENT}-CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "${COMPONENT}-PROJECT_BINARY_DIR:" ${PROJECT_BINARY_DIR})
    
    # state that anybody linking to us needs to include the current source dir
    # to find base_func.hpp, while we don't.
    # 调用程序链接inherit本目标时,需要包含base_func.hpp
    # 但是inherit本目标编译时,不需要链接base_func.hpp
    # 利用INTERFACE作用域限定include作用域:target本身不链接,传递到调用方链接
    # ${CMAKE_CURRENT_SOURCE_DIR}: The path to the source directory currently being processed. 
    # ${CMAKE_CURRENT_SOURCE_DIR}: 当前处理的源目录,即当前CMakeLists.txt所在目录
    # ${PROJECT_SOURCE_DIR}: 表示项目主CMakeLists.txt文件所在目录
    # ${CMAKE_INSTALL_INCLUDEDIR}: include
    # $: 表示库链接过程中需要包含的路径
    # $: 表示在安装目录下引用库需要包含的路径
    # 项目链接了${PROJECT_SOURCE_DIR}/libb_exports.hpp 故${PROJECT_SOURCE_DIR}作用域需要设置为PUBLIC
    target_include_directories(${COMPONENT}
                               INTERFACE
                               $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                               $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
                               $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
                               PUBLIC
                               $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
                               )
    
    # 添加BUILD_SHARED_LIBS判断是否编译动态库和静态库
    if(BUILD_SHARED_LIBS)
      # 在Windows平台下,动态库和静态库的导出符号需要区别对待
      target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBB_BUILD")
    endif()
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # link our compiler flags interface library
    # project_cxx_compiler_flags: 项目主CMakeLists.txt文件中定义的继承了C++特性的接口库
    # 链接接口库的编译标记
    # target_link_libraries(${COMPONENT} PUBLIC project_cxx_compiler_flags LIBA::inherit)
    target_link_libraries(${COMPONENT} PUBLIC LIBA::inherit)
    
    # 定义windows通用的导入导出符号,否则windows平台不会为dll生成导入库lib
    target_compile_definitions(${COMPONENT} PRIVATE "SHARED_LIBB_EXPORTING")
    
    # 为target添加DEBUG_POSTFIX属性
    set_target_properties(${COMPONENT} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # 安装lib
    # set(installable_libs ${COMPONENT} project_cxx_compiler_flags)
    set(installable_libs ${COMPONENT})
    
    # EXPORT可以导出其他CMake项目直接使用此项目的信息
    # 导出后可以将信息输出到xxx.cmake文件,方便其它项目find_package调用
    install(TARGETS ${installable_libs}
            EXPORT ${COMPONENT}-Targets
            LIBRARY DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            ARCHIVE DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>
            RUNTIME DESTINATION $<IF:$<CONFIG:Debug>,bind,bin>
            INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
            
    # 安装include
    # 方法一:
    # install(FILES usage_func.hpp DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${COMPONENT}")
    # 方法二:
    install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
            FILES_MATCHING PATTERN "*.hpp")
    
    # 生成和安装export file
    # generate and install export file
    install(EXPORT ${COMPONENT}-Targets
            FILE ${CMAKE_PROJECT_NAME}-${COMPONENT}Targets.cmake
            NAMESPACE ${CMAKE_PROJECT_NAME}::
            DESTINATION "cmake")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    libb_exports.hpp:
    #pragma once 
    
    #if (defined(_WIN32) || defined(_WIN64))
    #  if defined(SHARED_LIBB_BUILD)
    #    if defined(SHARED_LIBB_EXPORTING)
    #      define LIBB_DECLSPEC __declspec(dllexport)
    #    else
    #      define LIBB_DECLSPEC __declspec(dllimport)
    #    endif
    #  else // non shared
    #    define LIBB_DECLSPEC
    #  endif
    #else // non windows
    #  define LIBB_DECLSPEC
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    libb_config.hpp.in:
    // the configured options and settings for libb
    // 
    //# ${LIBB_VERSION_MAJOR} = ${PROJECT_VERSION_MAJOR}
    //# ${LIBB_VERSION_MINOR} = ${PROJECT_VERSION_MINOR}
    
    #pragma once
    
    #define LIBB_PROJ_VERSION "@PROJECT_VERSION@"
    #define LIBB_PROJ_VERSION_MAJOR @LIBB_VERSION_MAJOR@
    #define LIBB_PROJ_VERSION_MINOR @PROJECT_VERSION_MINOR@
    #define LIBB_PROJ_VERSION_PATCH @LIBB_VERSION_PATCH@
    #define LIBB_PROJ_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
    #define TEST_VERBOSE "@TEST_VERBOSE_FLAG@"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Config.cmake.in:
    @PACKAGE_INIT@
    
    #include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
    
    foreach(_dir ${CMAKE_CURRENT_LIST_DIR})
      message(STATUS "MONO CMAKE_CURRENT_LIST_DIR:" ${_dir})
    endforeach()
    
    # find dependency 
    # libb <- liba
    include(CMakeFindDependencyMacro)
    # needing LIBA's location
    #find_dependency(LIBA 1.1.0)
    find_dependency(LIBA 1.1.0 COMPONENTS inherit NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_LIST_DIR}/../../LIBA/cmake)
    
    set(_libb_supported_components redefining)
    
    # _FOUND=False
    # __FOUND=True
    
    foreach(_comp ${LIBB_FIND_COMPONENTS})
      if (NOT _comp IN_LIST _libb_supported_components)
        set(LIBB_FOUND False)
        set(LIBB_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
      endif()
      include("${CMAKE_CURRENT_LIST_DIR}/LIBB-${_comp}Targets.cmake")
    endforeach()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    CMakeLists.txt
    cmake_minimum_required(VERSION 3.20)
    
    # project指定项目名、版本号、编程语言
    # 关于软件版本命名规则:主版本号.子版本号.修订版本号.日期-阶段版本号
    # * 主版本号:当功能模块有较大的变动,比如增加多个模块或者整体架构发生变化。此版本号由项目决定是否修改。
    # * 子版本号:当功能有一定的增加或变化,比如增加了对权限控制、增加自定义视图等功能。此版本号由项目决定是否修改
    # * 修订版本号:一般是 Bug 修复或是一些小的变动,要经常发布修订版,时间间隔不限,修复一个严重的bug即可发布一个修订版。此版本号由项目经理决定是否修改。
    # * 日期-阶段版本号:日期用于记录修改项目的当前日期,此版本号由开发人员决定是否修改;阶段版本号用于标注当前版本的软件处于哪个开发阶段,此版本号由项目决定是否修改,一般是希腊字母。
    # 例如,1.1.12.20230117-Release
    # 阶段版本号说明:
    # * Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。
    # * Alpha:此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。
    # * Beta: 该版本相对于Alpha版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过多次测试来进一步消除。
    # * RC:该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。
    # * Release:该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。
    # 关于CMake的版本规则:MAJOR.MINOR.PATCH.TWEAK
    # CMake的版本号各个部分必须是数字组成
    # 例如:1.1.1.20231017
    project(LIBB VERSION 1.1.1.20231017 LANGUAGES C CXX)
    
    # make cache variables for install destinations
    include(GNUInstallDirs)
    
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # 指定C++编译标准
    # 方式一:利用set指定 the C++ standard
    # set(CMAKE_CXX_STANDARD 11)
    # set(CMAKE_CXX_STANDARD_REQUIRED True)
    # 方式二:利用接口库的形式
    # add_library(project_cxx_compiler_flags INTERFACE)
    # target_compile_features(project_cxx_compiler_flags INTERFACE cxx_std_11)
    
    # debug模式以'd'结尾
    set(CMAKE_DEBUG_POSTFIX d)
    
    # 指定自定义变量
    set(TEST_VERBOSE_FLAG "${PROJECT_NAME} Demo")
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # 判断当前使用的编译器
    # 基于生成器表达式
    # $: 当项目所用编程语言匹配language,
    # 且项目所用编译器匹配compiler_ids时,此表达式返回1,否则返回0
    # * Create a new variable gcc_like_cxx that is true if we are using CXX and
    #   any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
    # * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
    # set(gcc_like_cxx "$")
    # set(msvc_cxx "$")
    
    # project_cxx_compiler_flags可以从LIBA库可以传递过来
    # 为接口库project_cxx_compiler_flags 增加编译警告选项
    # 基于条件生成器表达式
    # * $ : 当condition为真则返回true_string,否则返回空字符串
    # * $ : 当condition为真则返回true_string,否则返回false_string
    # 生成器表达式支持嵌套
    # INTERFACE作用域表示project_cxx_compiler_flags 本身不需要,但是project_cxx_compiler_flags 调用方需要使用
    # BUILD_INTERFACE生成器表达式
    # 我们只想在编译阶段使用警告选项,而不会在安装阶段使用,可以利用BUILD_INTERFACE限制
    # target_compile_options(project_cxx_compiler_flags INTERFACE 
    #  "$<${gcc_like_cxx}:$>"
    #  "$<${msvc_cxx}:$>"
    # )
    
    # BUILD_SHARED_LIBS控制库类型的生成
    option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
    
    # configure a header file to pass some of the CMake settings
    # to the source code
    # 利用configure_file()命令可以实现文件复制,并且替换文件中@var@的变量值
    configure_file(libb_config.hpp.in ${PROJECT_BINARY_DIR}/include/libb_config.hpp)
    
    message(STATUS "PROJECT_NAME:" ${PROJECT_NAME})
    message(STATUS "CMAKE_PROJECT_NAME:" ${CMAKE_PROJECT_NAME})
    message(STATUS "CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "CMAKE_CURRENT_BINARY_DIR:" ${CMAKE_CURRENT_BINARY_DIR})
    
    message(STATUS "LIBB_BINARY_DIR:" ${LIBB_BINARY_DIR})
    message(STATUS "PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "CMAKE_SOURCE_DIR:" ${CMAKE_SOURCE_DIR})
    
    # 配置默认安装路径
    if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
      # ${CMAKE_SOURCE_DIR}表示源码树顶层目录路径
      set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/../install/${PROJECT_NAME})
    endif()
    
    # 添加redefining库
    add_subdirectory(${PROJECT_SOURCE_DIR}/redefining redefining_build)
    
    # 安装config.hpp exports.hpp
    install(FILES "${PROJECT_BINARY_DIR}/include/libb_config.hpp" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    install(FILES "${PROJECT_SOURCE_DIR}/libb_exports.hpp" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    
    # include CMakePackageConfigHelpers macro
    include(CMakePackageConfigHelpers)
    
    # 针对target设置版本属性
    #set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${PROJECT_VERSION})
    #set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR})
    #set_property(TARGET ${PROJECT_NAME} PROPERTY
    #  INTERFACE_${PROJECT_NAME}_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
    #set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY
    #  COMPATIBLE_INTERFACE_STRING ${PROJECT_NAME}_MAJOR_VERSION
    #)
    
    # generate the version file for the config file
    write_basic_package_version_file(
      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
      VERSION "${PROJECT_VERSION}"
      COMPATIBILITY AnyNewerVersion
    )
    # generate the config file that includes the exports
    configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
      INSTALL_DESTINATION "cmake"
      NO_SET_AND_CHECK_MACRO
      NO_CHECK_REQUIRED_COMPONENTS_MACRO
    )
    # install config files
    install(FILES
      ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
      ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
      DESTINATION "cmake"
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125

    tutorial库-内容

    main.cxx:
    // A simple program that computes the square root of a number
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char* argv[])
    {
        if (argc < 2) {
            // report version
            std::cout << argv[0] << "liba version:" << LIBA_PROJ_VERSION
                    << " --- libb version:" << LIBB_PROJ_VERSION << std::endl;
            std::cout << "Usage: " << argv[0] << " number" << std::endl;
            return 1;
        }
    
        // convert input to double
        const double iv = std::stod(argv[1]);
    
        std::cout << "liba::CUsageFunction: " << std::endl;
        liba::CUsageFunction uf;
        double ufv = uf.sqrt(iv);
        std::cout << "The square root of " << iv << " is " << ufv
                << std::endl;
        uf.message();
    
        std::cout << "libb::CDerivedFunction: " << std::endl;
        libb::CDerivedFunction df;
        double dfv = df.sqrt(iv);
        std::cout << "The square root of " << iv << " is " << dfv
                << std::endl;
        df.message();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    CMakeLists.txt:
    cmake_minimum_required(VERSION 3.20)
    
    # project指定项目名、版本号、编程语言
    # 关于软件版本命名规则:主版本号.子版本号.修订版本号.日期-阶段版本号
    # * 主版本号:当功能模块有较大的变动,比如增加多个模块或者整体架构发生变化。此版本号由项目决定是否修改。
    # * 子版本号:当功能有一定的增加或变化,比如增加了对权限控制、增加自定义视图等功能。此版本号由项目决定是否修改
    # * 修订版本号:一般是 Bug 修复或是一些小的变动,要经常发布修订版,时间间隔不限,修复一个严重的bug即可发布一个修订版。此版本号由项目经理决定是否修改。
    # * 日期-阶段版本号:日期用于记录修改项目的当前日期,此版本号由开发人员决定是否修改;阶段版本号用于标注当前版本的软件处于哪个开发阶段,此版本号由项目决定是否修改,一般是希腊字母。
    # 例如,1.1.12.20230117-Release
    # 阶段版本号说明:
    # * Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。
    # * Alpha:此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。
    # * Beta: 该版本相对于Alpha版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过多次测试来进一步消除。
    # * RC:该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。
    # * Release:该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。
    # 关于CMake的版本规则:MAJOR.MINOR.PATCH.TWEAK
    # CMake的版本号各个部分必须是数字组成
    # 例如:1.1.1.20231017
    project(TUTORIAL VERSION 1.1.1.20231017 LANGUAGES C CXX)
    
    # make cache variables for install destinations
    include(GNUInstallDirs)
    
    # 指定C++编译标准
    # 方式一:利用set指定 the C++ standard
    # set(CMAKE_CXX_STANDARD 11)
    # set(CMAKE_CXX_STANDARD_REQUIRED True)
    # 方式二:利用接口库的形式
    add_library(tutorial_cxx_compiler_flags INTERFACE)
    target_compile_features(tutorial_cxx_compiler_flags INTERFACE cxx_std_11)
    
    # debug模式以'd'结尾
    set(CMAKE_DEBUG_POSTFIX d)
    
    # 指定自定义变量
    set(TEST_VERBOSE_FLAG "${PROJECT_NAME} Demo")
    
    # 判断当前使用的编译器
    # 基于生成器表达式
    # $<COMPILE_LANG_AND_ID:language,compiler_ids>: 当项目所用编程语言匹配language,
    # 且项目所用编译器匹配compiler_ids时,此表达式返回1,否则返回0
    # * Create a new variable gcc_like_cxx that is true if we are using CXX and
    #   any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
    # * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
    set(gcc_like_cxx "$")
    set(msvc_cxx "$")
    
    # 为接口库tutorial_cxx_compiler_flags 增加编译警告选项
    # 基于条件生成器表达式
    # * $<condition:true_string> : 当condition为真则返回true_string,否则返回空字符串
    # * $<IF:condition,true_string,false_string> : 当condition为真则返回true_string,否则返回false_string
    # 生成器表达式支持嵌套
    # INTERFACE作用域表示tutorial_cxx_compiler_flags 本身不需要,但是tutorial_cxx_compiler_flags 调用方需要使用
    # BUILD_INTERFACE生成器表达式
    # 我们只想在编译阶段使用警告选项,而不会在安装阶段使用,可以利用BUILD_INTERFACE限制
    target_compile_options(tutorial_cxx_compiler_flags INTERFACE 
      "$<${gcc_like_cxx}:$>"
      "$<${msvc_cxx}:$>"
    )
    
    message(STATUS "PROJECT_NAME:" ${PROJECT_NAME})
    message(STATUS "CMAKE_PROJECT_NAME:" ${CMAKE_PROJECT_NAME})
    message(STATUS "CMAKE_CURRENT_SOURCE_DIR:" ${CMAKE_CURRENT_SOURCE_DIR})
    message(STATUS "CMAKE_CURRENT_BINARY_DIR:" ${CMAKE_CURRENT_BINARY_DIR})
    
    message(STATUS "LIBB_BINARY_DIR:" ${LIBB_BINARY_DIR})
    message(STATUS "PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR})
    message(STATUS "CMAKE_SOURCE_DIR:" ${CMAKE_SOURCE_DIR})
    
    
    # 添加路径到CMAKE_PREFIX_PATH 使find_package可以搜索加载package
    list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../install/LIBA/cmake")
    list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../install/LIBB/cmake")
    
    foreach(_dir ${CMAKE_PREFIX_PATH})
      message(STATUS "${CMAKE_PROJECT_NAME}-find_package in dir:${_dir}")
    endforeach()
    
    # TUTORIAL <-- LIBB <-- LIBA
    # TUTORIAL <-- LIBA
    # LIBB <-- LIBA
    find_package(LIBB 1.1.0 REQUIRED COMPONENTS redefining)
    if(NOT LIBB_FOUND)
      message(FATAL_ERROR "${COMPONENT}-package: LIBB not found!")
    endif()
    
    find_package(LIBA 1.1.0 REQUIRED COMPONENTS call)
    if(NOT LIBA_FOUND)
      message(FATAL_ERROR "${COMPONENT}-package: LIBA not found!")
    endif()
    
    # add the executable
    add_executable(${PROJECT_NAME} main.cxx)
    
    target_link_libraries(${PROJECT_NAME} LIBB::redefining LIBA::call tutorial_cxx_compiler_flags)
    
    # 为target添加DEBUG_POSTFIX属性
    set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    构建使用示例:

    以Release模式为例:先构建liba,再构建libb,最后构建tutorial

    liba-构建命令

    > cd @liba目录
    > mkdir build_release
    > cd build_release
    > cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release ..
    > cmake --build .
    > cmake --install .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    libb-构建命令

    > cd @libb目录
    > mkdir build_release
    > cd build_release
    > cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release ..
    > cmake --build .
    > cmake --install .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    tutorial-构建命令

    > cd @liba目录
    > mkdir build_release
    > cd build_release
    > cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release ..
    > cmake --build .
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:针对multi-configuration生成器,比如Visual Studio,是会忽略CMAKE_BUILD_TYPE选项的;只有single-configuration生成器,比如Makefile,才支持CMAKE_BUILD_TYPE选项。
    故在CMakeLists.txt中判断Debug模式还是Release模式,需要使用生成器表达式:

    $<IF:$<CONFIG:Debug>,true_string,false_string>
    
    • 1
  • 相关阅读:
    小白终于解决了在学习Go中不知道Makefile是什么的难题
    如何获取文本中出现的所有单词
    Java线程池ThreadPoolExecutor详解(一篇就够了)
    工业机器视觉系统构成及功能
    干涉阵相关知识
    【已解决】Vue3+Element-plus中input输入框中图标不显示
    【Node.js从基础到高级运用】七、基本的网络编程
    微服务自动化【集群搭建】
    springboot大学生兼职网站开发与设计毕业设计源码311734
    PyTorch创始人:开源成功的方法论
  • 原文地址:https://blog.csdn.net/xys206006/article/details/133909570