学习C++:C++进阶(三)CMake基础篇---用cmake配置静态库、共享库、仅有头文件的库、对象库并使用这些库

目录

1.1 通过一个案例总结前三章的内容(Summarize the content of the first three chapters with a case)

1.1.1 项目源码及文件(Project source code and files)

1.1.2 构建并创建项目(Build and create the project)

1.1.3 用cmake构建仅有头文件的库(Building a header-only library with cmake)

1.1.4 用cmake构建对象库(Building object libraries with cmake)

1.1.5 用cmake构建共享库(Building shared libraries with cmake)

1.4.6 用cmake构建静态库(Build static library with cmake)

1.1.7 外部调用这些创建好的库(call these created libraries externally)​​​​​​​


1.1 通过一个案例总结前三章的内容(Summarize the content of the first three chapters with a case)

1.1.1 项目源码及文件(Project source code and files)

GitHub - PacktPublishing/CMake-Best-Practices: CMake Best Practices, by Packt PublishingCMake Best Practices, by Packt Publishing. Contribute to PacktPublishing/CMake-Best-Practices development by creating an account on GitHub.https://github.com/PacktPublishing/CMake-Best-Practices这里面的chapter3文件夹

1.1.2 构建并创建项目(Build and create the project)

1.配置:通过命令 

2. 构建:通过命令

1.1.3 用cmake构建仅有头文件的库(Building a header-only library with cmake)

1.hello_header_only结构解析(仅有头文件的库)

        作为一个只有头文件的库,仅通过hpp文件创建自己的库。

         chapter03/hello_header_only/include/hello_header_only/*.hpp

#pragma once

#include <iostream>
#include <string>

namespace hello_header_only
{
    void print_hello(const std::string& name) {
        std::cout << "Hello " << name << " from a header only library\n";
    }
}

        我们来解释它的CMakLists.txt文件。

# Chapter 3 - Example illustrating how to create a header only library
#
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.21)

project(
    ch3_hello_header_only
    VERSION 1.0
    DESCRIPTION "Chapter 3 header only example"
    LANGUAGES CXX
)

# 创建库目标
add_library(ch3_hello_header_only INTERFACE)

# 公开包含目录
target_include_directories(ch3_hello_header_only INTERFACE include/)

# 公开编译此库所需的最低 C++ 版本
target_compile_features(ch3_hello_header_only INTERFACE cxx_std_17)

        这个介绍了如何创建一个仅仅有头文件.hpp的库的CMakLists.txt的写法。

        cmake_minimum_required说明表明了他要求cmake版本最低为3.21。

        project字段说明了这个项目的名称为ch3_hello_header_only,版本为1.0,项目描述字符串为Chapter 3 header only example,用C++构建。

        add_library字段说明了生成一个名字为ch3_hello_header_only的库,我们看看buid文件里但没有指定是静态还是动态库。

        target_include_directories设置库的包含目录,即这个库的源文件在include下,我们看下文件结构,也是如此。

         这样这个库就设计好了。

1.1.4 用cmake构建对象库(Building object libraries with cmake)

1. hello_object_lib结构解析(对象库解析)

        这个稍有一点复杂,我们查看其目录树:

2.库文件代码解读

#pragma once

#include <string>

namespace hello_object {
/// 显式导出到 dll 中的示例类
class HelloObject {
public:
  HelloObject(const std::string &name) : name_{name} {}

  void greet() const;

private:
  const std::string name_;
};
} // namespace hello_object

        这里有一个类,一个类的默认构造函数,另一个是greet方法,我们在源文件看其实现:

#include <hello_object/hello_object.hpp>

#include <iostream>

namespace hello_object {
void HelloObject::greet() const {
  std::cout << "Hello " << name_ << " From an object library\n";
}
} // namespace hello_object

        greet函数输出一行字。

3.CMakLists.txt文件解读

# Chapter3-  Example illustrating how to create an object library
#
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.17)

project(
    ch3_hello_object
    VERSION 1.0.0
    DESCRIPTION
        "A simple C++ project to demonstrate creating executables and libraries in CMake"
    LANGUAGES CXX
)

# 添加库目标
add_library(ch3_hello_object OBJECT)

# 将源添加到库目标
target_sources(ch3_hello_object PRIVATE src/hello_object.cpp)

# 定义编译该库并使其对依赖者可见所需的 C++ 标准
target_compile_features(ch3_hello_object PUBLIC cxx_std_17)

# 设置包含目录
target_include_directories(
    ch3_hello_object
    PRIVATE src/hello
    PUBLIC include
)

        add_library关键字:创立一个名叫ch3_hello_object的库,是一个对象库。

        target_sources关键字:添加库的源文件;第一个参数是库的名称,第二个指明私有,第三个是库源文件的路径,这时,库文件和源文件链接起来了。

        target_include_directories:设置库的包含目录,库下面有俩文件夹,源文件设置为私有,库文件设置为公共权限。

1.1.5 用cmake构建共享库(Building shared libraries with cmake)

1.目录树解析

2.分析源码

        我们看看代码这个库究竟在干什么呢?

        首先看hello.hpp

#pragma once 

#include <string>
#include "hello/export_hello.hpp"


namespace hello{
    /// 显式导出到 dll 中的示例类
    class CH3_HELLO_SHARED_EXPORT Hello {
        public:
        Hello(const std::string& name) : name_{name} {}

        void greet() const; 
        private:
        const std::string name_;
    };
}

        hello.cpp

#include <hello/hello.hpp>

#include "internal.hpp"

namespace hello{
    void Hello::greet() const {
        details::print_impl(name_);
    }
}

        internal.hpp

#pragma once 

#include <string>

namespace hello::details{

    void print_impl(const std::string& name);
    
}

        internal.cpp

#include "internal.hpp"

#include <iostream>

namespace hello::details{ 
    void print_impl(const std::string& name)
    {
        std::cout << "Hello " << name << " from a shared library\n";
    }
}

        这里internal的实现包含一个hello::details命名空间下的一个print_impl符号,作用是打印默认构造传进来的string类型的参数。

        hello中通过引入头文件导出了internal中的print_impl符号,但怎么引出来的呢?我们这时看它的CMakLists.txt文件:

3.分析cmakelists.txt文件

# Chapter3- example illustrating how to create a shared library
# Additionally the example shows how to set a postfix for debug libraries and 
# how to handle symbol visibilities for shared libraries
#
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.17)

project(
    ch3_hello_shared
    VERSION 1.0.0
    DESCRIPTION
        "A simple C++ project to demonstrate creating executables and libraries in CMake"
    LANGUAGES CXX
)

# 在调试模式下构建库时,为生成的 .so 或 .dll 文件设置后缀“d”
set(CMAKE_DEBUG_POSTFIX
    d
)

# 添加库目标
add_library(ch3_hello_shared SHARED)

# 为目标设置属性。 VERSION 将库版本设置为项目版本 * SOVERSION 将库的兼容版本设置为版本的主编号
set_target_properties(
    ch3_hello_shared
    PROPERTIES VERSION ${PROJECT_VERSION}
               SOVERSION ${PROJECT_VERSION_MAJOR}
)

# 将源添加到库目标
target_sources(
    ch3_hello_shared
    PRIVATE src/hello.cpp src/internal.cpp
)

# 定义编译该库并使其对依赖者可见所需的 C++ 标准
target_compile_features(
    ch3_hello_shared
    PUBLIC cxx_std_17
)

# 设置包含目录
target_include_directories(
    ch3_hello_shared
    PRIVATE src/ch3_hello_shared
    PUBLIC include
)

# 如果使用有限的可见性,将 CXX_VISIBILILTY_PRESET 设置为“隐藏”
include(GenerateExportHeader)
set_property(
    TARGET ch3_hello_shared
    PROPERTY CXX_VISIBILITY_PRESET "hidden"
)

# 默认隐藏内联函数,减少库的大小
set_property(
    TARGET ch3_hello_shared
    PROPERTY VISIBILITY_INLINES_HIDDEN TRUE
)

# 此命令在 CMAKE_CURRENT_BINARY_DIR 中生成一个头文件,该文件根据编译器设置设置可见性属性
generate_export_header(
    ch3_hello_shared
    EXPORT_FILE_NAME
    export/hello/export_hello.hpp
)

# 将 CMAKE_CURRENT_BINARY_DIR 添加到包含路径,以便可以找到生成的标头
target_include_directories(
    ch3_hello_shared
    PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/export"
)

# 导出符号的另一种但不推荐的方式是使用如下命令
# set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) Which exports all dll symbols by
# default

        这个比较长,我们一条条来解释。

1.cmake_minimum_required(VERSION 3.17)   指定cmake最低版本为3.17

2.project字段    指定项目名称为ch3_hello_shared、库的版本为1.0.0、用C++构建。

3.set(CMAKE_DEBUG_POSTFIX d)   如果编译模式为debug,将库的名字后面加上d,我们改称后缀名为s试一试,果然后缀有一个s。

cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build
cmake --build ./build

4.add_library(ch3_hello_shared SHARED)   创建共享库ch3_hello_shared

5.set_target_properties    有几种“重载”用法:

用法1是更改库的默认名称例如 Linux 上的 lib<name>.so 和 Windows 上的 <name>.lib 或 <name>.dll,可以通过修改默认行为更改库的名字,比如

set_target_properties(
   ch3_hello_shared
   PROPERTIES OUTPUT_NAME hello
)

将输出文件的名字由ch3_hello.dll变成了hello.dll。

第二种重载用法是共享库的命名约定,将版本添加到文件名中指定构建版本和api版本,为目标设置属性。 VERSION 将库版本设置为项目版本 * SOVERSION 将库的兼容版本设置为版本的主编号。
//Value Computed by CMake
CMAKE_PROJECT_VERSION:STATIC=1.0.0

//Value Computed by CMake
CMAKE_PROJECT_VERSION_MAJOR:STATIC=1

因此共享库的名字就是libch3_hello_shared.1.0.0.shared

6.target_sources  库文件中的源文件

7.target_include_directories 设置库文件的包含目录

8.set_property  如果要设置默认隐藏性,将CXX_VISIBILITY_PRESET参数设置为hidden

9.generate_export_header :生成导出头文件

generate_export_header(
    ch3_hello_shared
    EXPORT_FILE_NAME
    export/hello/export_hello.hpp

        HELLO_EXPORT 定义将包含有关在编译库时是否要导出符号或在链接库时是否应导入符号的信息。

        hello 库的符号默认设置为隐藏。然后,使用 GenerateExportHeader 模块导入的 generate_export_header 宏再次单独启用它们。

        此命令在 CMAKE_CURRENT_BINARY_DIR 中生成一个头文件,该文件根据编译器设置设置可见性属性。

10.target_include_directories 包含这个导出头文件,所有符号均可见。

target_include_directories(
    ch3_hello_shared
    PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/export"
)

这个导出文件是在build/export目录。9、10用的均是相对于CMAKE_CURRENT_BINARY_DIR的目录。

1.4.6 用cmake构建静态库(Build static library with cmake)​​​​​​​

1.目录树解析

2.代码解析 

        看它的代码,了解它做了什么:

 hello.hpp:在命名空间hello下声明一个Hello类,有一个Hello构造函数、一个greet函数、一个私有属性string name_。

#pragma once 

#include <string>

namespace hello{
    /// Example class that is explicitly exported into a dll
    class Hello {
        public:
        Hello(const std::string& name) : name_{name} {}

        void greet() const; 
        private:
        const std::string name_;
    };
}

hello.cpp:实现了hello.hpp的greet函数。并且调用一个外部符号。

#include <hello/hello.hpp>

#include "internal.hpp"

namespace hello{
    void Hello::greet() const {
        details::print_impl(name_);
    }
}

internal.hpp:定义了hello::details定义域下的print_impl符号

#pragma once 

#include <string>

namespace hello::details{

    void print_impl(const std::string& name);
    
}

internal.cpp:实现了print_impl

#include "internal.hpp"

#include <iostream>

namespace hello::details{ 
    void print_impl(const std::string& name)
    {
        std::cout << "Hello " << name << " from a shared library\n";
    }
}

3.cmakelists.txt解析 

看它的cmakelists.txt文件: 

# Chapter3-  Illustriating how to create a static library
#
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.17)

project(
    ch3_hello_static
    VERSION 1.0.0
    DESCRIPTION
        "A simple C++ project to demonstrate creating executables and libraries in CMake"
    LANGUAGES CXX
)

# 添加库目标
add_library(ch3_hello_static STATIC)

# 将目标的输出设置为`hello`,这样它将在windows上保存为hello.dll,在linux上保存为libhello.a
set_target_properties(
    ch3_hello_static
    PROPERTIES OUTPUT_NAME hello
)


# 为目标设置属性。 VERSION 将库版本设置为项目版本 * SOVERSION 将库的兼容版本设置为版本的主编号
set_target_properties(
    ch3_hello_static
    PROPERTIES VERSION ${PROJECT_VERSION}
               SOVERSION ${PROJECT_VERSION_MAJOR}
)

# 将源添加到库目标
target_sources(
    ch3_hello_static
    PRIVATE src/hello.cpp src/internal.cpp
)

# 定义编译该库并使其对依赖者可见所需的 C++ 标准
target_compile_features(
    ch3_hello_static
    PUBLIC cxx_std_17
)

# 设置包含目录
target_include_directories(
    ch3_hello_static
    PRIVATE src/ch3_hello_static
    PUBLIC include
)

        由于共享库的困难版本我们已经详细介绍,这里不再阐述。

        我们看一下编译后的结果:

库名字改变了并成功形成了静态库。

1.1.7 外部调用这些创建好的库(call these created libraries externally)​​​​​​​

1.我们先看目录树

liuhongwei@liuhongwei-Lenovo-Legion-R9000P2021H:~/桌面/CMake-Best-Practices-main
/chapter03$ tree
.
├── CMakeLists.txt
├── hello_header_only
│   ├── CMakeLists.txt
│   └── include
│       └── hello_header_only
│           └── hello_header_only.hpp
├── hello_object_lib
│   ├── CMakeLists.txt
│   ├── include
│   │   └── hello_object
│   │       └── hello_object.hpp
│   └── src
│       └── hello_object.cpp
├── hello_shared_lib
│   ├── CMakeLists.txt
│   ├── include
│   │   └── hello
│   │       └── hello.hpp
│   └── src
│       ├── hello.cpp
│       ├── internal.cpp
│       └── internal.hpp
├── hello_static_lib
│   ├── CMakeLists.txt
│   ├── include
│   │   └── hello
│   │       └── hello.hpp
│   └── src
│       ├── hello.cpp
│       ├── internal.cpp
│       └── internal.hpp
├── hello_world_standalone
│   ├── CMakeLists.txt
│   └── src
│       └── main.cpp
└── src
    └── main.cpp

18 directories, 19 files

2.main.cpp

#include <hello/hello.hpp>
#include <hello_header_only/hello_header_only.hpp>
#include <hello_object/hello_object.hpp>

int main(int, char **) {
  hello_header_only::print_hello("John Doe");
  hello::Hello hello("Jane Doe");
  hello.greet();
  return 0;
}

头文件是怎么导入的?

<hello/hello.hpp>是导入静态库(静态库的target_include_directories里声明了对外界的接口),因为静态库的include下有hello目录,hello目录里面包含hello.hpp。

其他两条比较好理解不再阐述。

3.cmakelists.txt解析

# Top-level CMakeLists file for the Chapter 3 example content.
#
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.21)

project(
    chapter_3
    VERSION 1.0
    DESCRIPTION
        "A collection of sample C++ applications and libraries to demonstrate creating libraries and executables"
    LANGUAGES CXX
)

# 添加带有示例的子目录
add_subdirectory(hello_world_standalone)
add_subdirectory(hello_shared_lib)
add_subdirectory(hello_static_lib)
add_subdirectory(hello_header_only)
add_subdirectory(hello_object_lib)

# 添加示例可执行文件
add_executable(chapter3)

# 将源代码添加到示例可执行文件
target_sources(chapter3 PRIVATE src/main.cpp)

# 将库“hello”和“hello_header_only”链接到示例可执行文件,这些库在子目录中进行了描述
target_link_libraries(chapter3 PRIVATE ch3_hello_header_only ch3_hello_shared ch3_hello_object)

1.cmake_minimum_required 定义cmake最低版本

2. add_subdirectory 添加库的目录

3.target_link_libraries 将项目与库关联

猜你喜欢

转载自blog.csdn.net/qq_41694024/article/details/125811792