CMake:静态库和动态的补充

CMake构建静态库与动态库模板探索

导言

上一篇,我们分别学习了利用CMake去构建多文件多目录的项目,然后分别讲述了静态库和动态库的相关知识,最后学习在不同平台(WindowsLinux)上利用CMake去构建项目。具体可参考上一篇

今天,我们将对CMake在不同平台上构建动态库和静态库做进一步的探索,即如何利用一个比较统一的模板在不同的平台构建静态库和动态库,以及对add_library命令的其他参数做进一步的探索。

库模板

1. 项目结构

.
├── cmake
│	└── message_config.cmake.in
├── message-module  
│   ├── include
│   │	├── message_export_lib.h
│   │   └── message.h
│   ├── src
│   │   └── message.cpp
│   └── CMakeLists.txt
├── hello_world.cpp
└── CMakeLists.txt

本项目的结构相对比较复杂,为了能够生成一套比较标准的库(静态库/动态库),所以项目中会包含很多配置项,接下来我们会对项目中的所有内容进行一一讲解。

项目地址

2. 根目录下的CMakeLists.txt

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)

project(hello-world LANGUAGES CXX)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

option(BUILD_SHARED_LIBS "Specifies the type of libraries (SHARED or STATIC) to build" OFF)

# Set install direcotory
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/output/)
endif()

add_subdirectory(${CMAKE_SOURCE_DIR}/message-module)

include_directories(
    ${CMAKE_SOURCE_DIR}/message-module/include
)

add_executable(
    ${PROJECT_NAME}
    ${CMAKE_SOURCE_DIR}/hello_world.cpp
) 

target_link_libraries(
    ${PROJECT_NAME}
    test_message   
)
option(BUILD_SHARED_LIBS "Specifies the type of libraries (SHARED or STATIC) to build" OFF)

首先,我们提供了在执行cmake命令时的参数选项,默认默认情况下BUILD_SHARED_LIBS的状态的是关闭的(OFF),即默认情况下我们默认构建的是静态库。

当我们要构建动态库时,我们需执行以下命令(假设你已经在项目中构建了build文件夹,并且你现在在build目录中)

cmake .. -DBUILD_SHARED_LIBS=ON

这样我们便使得宏BUILD_SHARED_LIBSCMake中可以使用了。

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/output/)
endif()

如果我们没有指定在make install后的输出路径,则CMAKE_INSTALL_PREFIX 将设置为${CMAKE_SOURCE_DIR}/output/,即当前项目下的output目录。

3. cmake目录下的message_config.cmake.in

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/message_config.cmake" )

为了能够生成一个标准的库,我们写了一个message_config.cmake.in文件,该文件执行make install时将被调用,调用时我们再进行讲解。

4. message-module下头文件message_export_lib.h

#ifndef MESSAGE_EXPORT_LIB_H_
#define MESSAGE_EXPORT_LIB_H_

#ifdef MESSAGE_LIB_SHARED_BUILD
    #ifdef _WIN32
        #ifdef MESSAGE_LIB_EXPORTS
            #define MESSAGE_LIB_API __declspec(dllexport)
        #else
            #define MESSAGE_LIB_API __declspec(dllimport)
        #endif  // MESSAGE_LIB_EXPORTS
    #else
        #define MESSAGE_LIB_API
    #endif  // _WIN32
#else
    #define MESSAGE_LIB_API
#endif  // MESSAGE_LIB_SHARED_BUILD

#endif // ! MESSAGE_EXPORT_LIB_H_

首先,如果我们定义了宏定义MESSAGE_LIB_SHARED_BUILD(即我们要构建动态库时),我们将执行以下命令:如果时Windows平台,并且是在生成动态库时,即定义了(MESSAGE_LIB_EXPORTS),将__declspec(dllexport)定义为MESSAGE_LIB_API;如果是使用动态库时,即未定义(MESSAGE_LIB_EXPORTS),则将__declspec(dllimport)定义为MESSAGE_LIB_API。如果是非Windows平台,则对宏MESSAGE_LIB_API不做任何操作(因为在非Windows平台上可以动态库的生成和使用与静态库是一样的)。

然后,如果我们未定义宏定义MESSAGE_LIB_SHARED_BUILD(即我们要构建静态库时),我们对宏MESSAGE_LIB_API不做任何操作。

注意:关于宏定义MESSAGE_LIB_SHARED_BUILDMESSAGE_LIB_EXPORTS是否要添加利用message-module下的CMakeLists.txt进行配置。

5. message-module下头文件message.h

#ifndef MESSAGE_HEADER_H_
#define MESSAGE_HEADER_H_

#include <iostream>
#include <string>

#include "message_export_lib.h"
class MESSAGE_LIB_API Message {
 public:
  Message() {}

  void Print(const std::string &message);
};

我们将头文件message_export_lib.h包含进来,来控制在不同平台生成不同的库时的选项。

6. message-module下的CMakeLists.txt

file(GLOB SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

if (BUILD_SHARED_LIBS)
    add_library(test_message SHARED ${SOURCE_FILE})
    target_compile_definitions(test_message PUBLIC -DMESSAGE_LIB_SHARED_BUILD)
    target_compile_definitions(test_message PRIVATE -DMESSAGE_LIB_EXPORTS)
else()
    add_library(test_message STATIC ${SOURCE_FILE})
endif()

# 添加别名,以便库可以在构建树中使用,例如在测试时
add_library(test_message::test_message ALIAS test_message)

target_include_directories(test_message
    PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/include
    $<INSTALL_INTERFACE:include>
)

set_target_properties(test_message PROPERTIES 
    CXX_STANDARD 11
    CMAKE_CXX_STANDARD_REQUIRED True
)

install(TARGETS test_message
        EXPORT message_export_target
        RUNTIME DESTINATION "bin"
        LIBRARY DESTINATION "lib"
        ARCHIVE DESTINATION "lib"
)

install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ 
        DESTINATION "include"
        FILES_MATCHING PATTERN "*.h"
)

install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ 
        DESTINATION "include"
        FILES_MATCHING PATTERN "*.hpp"
)

install(EXPORT message_export_target
        FILE message_lib.cmake
        DESTINATION lib/cmake/test_message
)


include(CMakePackageConfigHelpers)
# generate the config file that is includes the exports
configure_package_config_file(
    ${CMAKE_SOURCE_DIR}/cmake/message_config.cmake.in
    "${CMAKE_SOURCE_DIR}/cmake/message_config.cmake"
    INSTALL_DESTINATION "lib/cmake/test_message"
)

# generate the version file for the config file
write_basic_package_version_file(
    "${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake"
    VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
    COMPATIBILITY AnyNewerVersion
)

install(FILES
    ${CMAKE_SOURCE_DIR}/cmake/message_config.cmake
    ${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake
    DESTINATION lib/cmake/test_message
)

export(EXPORT message_export_target FILE ${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake)
if (BUILD_SHARED_LIBS)
    add_library(test_message SHARED ${SOURCE_FILE})
    target_compile_definitions(test_message PUBLIC -DMESSAGE_LIB_SHARED_BUILD)
    target_compile_definitions(test_message PRIVATE -DMESSAGE_LIB_EXPORTS)
else()
    add_library(test_message STATIC ${SOURCE_FILE})
endif()

如果我们让宏BUILD_SHARED_LIBS为开启状态(即我们要构建动态库),我们要将宏定义MESSAGE_LIB_SHARED_BUILDMESSAGE_LIB_EXPORTS添加到编译器中,这样我们的头文件message_export_lib.h中便知道我们要构建动态库。

否则(即我们没有让BUILD_SHARED_LIBS开启),我们将构建静态库。

install(EXPORT message_export_target
        FILE message_lib.cmake
        DESTINATION lib/cmake/test_message
)


include(CMakePackageConfigHelpers)
# generate the config file that is includes the exports
configure_package_config_file(
    ${CMAKE_SOURCE_DIR}/cmake/message_config.cmake.in
    "${CMAKE_SOURCE_DIR}/cmake/message_config.cmake"
    INSTALL_DESTINATION "lib/cmake/test_message"
)

# generate the version file for the config file
write_basic_package_version_file(
    "${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake"
    VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
    COMPATIBILITY AnyNewerVersion
)

install(FILES
    ${CMAKE_SOURCE_DIR}/cmake/message_config.cmake
    ${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake
    DESTINATION lib/cmake/test_message
)

export(EXPORT message_export_target FILE ${CMAKE_SOURCE_DIR}/cmake/message_config_version.cmake)

这将利用CMakePackageConfigHelpers模块生成关于test_message库的cmake的配置文件,且可以使用命令find_package命令找到库test_message库。关于以上命令的具体参数使用,我们将在具体的命令学习章节进行具体的讲解和学习。

add_library其他参数的一些探索

1. 生成对象库

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

project(recipe-03 LANGUAGES CXX)

add_library(message-objs
    OBJECT
        Message.hpp
        Message.cpp
    )
# this is only needed for older compilers
# but doesn't hurt either to have it
set_target_properties(message-objs
    PROPERTIES
        POSITION_INDEPENDENT_CODE 1
    )

add_library(message-shared
    SHARED
        $<TARGET_OBJECTS:message-objs>
    )

add_library(message-static
    STATIC
        $<TARGET_OBJECTS:message-objs>
    )

add_executable(hello-world hello-world.cpp)

target_link_libraries(hello-world message-static)

为了保证编译的目标文件与生成位置无关,可以通过使用set_target_properties命令,设置message-objs目标的相应属性来实现。

注意:可能在某些平台和/或使用较老的编译器上,需要显式地为目标设置POSITION_INDEPENDENT_CODE属性。

现在,可以使用这个对象库来获取静态库(message-static)和动态库(message-shared)。要注意引用对象库的生成器表达式语法:$<TARGET_OBJECTS:message-objs>。生成器表达式是CMake在生成时(即配置之后)构造,用于生成特定于配置的构建输出。

2. 将静态库和动态库同时命名为同名的两个库

add_library(message-shared
  SHARED
    $<TARGET_OBJECTS:message-objs>
    )
set_target_properties(message-shared
    PROPERTIES
        OUTPUT_NAME "message"
    )
add_library(message-static
    STATIC
        $<TARGET_OBJECTS:message-objs>
    )
set_target_properties(message-static
    PROPERTIES
        OUTPUT_NAME "message"
    )

更多请关注微信公众号【Hope Hut】:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值