目录
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.4.6 用cmake构建静态库(Build static library with cmake)
1.1.7 外部调用这些创建好的库(call these created libraries externally)
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.配置:通过命令
2. 构建:通过命令
1.hello_header_only结构解析(仅有头文件的库)
作为一个只有头文件的库,仅通过hpp文件创建自己的库。
chapter03/hello_header_only/include/hello_header_only/*.hpp
#pragma once #include
#include 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. hello_object_lib结构解析(对象库解析)
这个稍有一点复杂,我们查看其目录树:
2.库文件代码解读
#pragma once #include
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
#include 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.目录树解析
2.分析源码
我们看看代码这个库究竟在干什么呢?
首先看hello.hpp
#pragma once #include
#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
#include "internal.hpp" namespace hello{ void Hello::greet() const { details::print_impl(name_); } } internal.hpp
#pragma once #include
namespace hello::details{ void print_impl(const std::string& name); } internal.cpp
#include "internal.hpp" #include
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
.so 和 Windows 上的 .lib 或 .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.目录树解析
2.代码解析
看它的代码,了解它做了什么:
hello.hpp:在命名空间hello下声明一个Hello类,有一个Hello构造函数、一个greet函数、一个私有属性string name_。
#pragma once #include
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
#include "internal.hpp" namespace hello{ void Hello::greet() const { details::print_impl(name_); } } internal.hpp:定义了hello::details定义域下的print_impl符号
#pragma once #include
namespace hello::details{ void print_impl(const std::string& name); } internal.cpp:实现了print_impl
#include "internal.hpp" #include
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.我们先看目录树
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
#include #include int main(int, char **) { hello_header_only::print_hello("John Doe"); hello::Hello hello("Jane Doe"); hello.greet(); return 0; } 头文件是怎么导入的?
是导入静态库(静态库的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 将项目与库关联