一、关于Static Library

Static Library 直译过来就是“静态库”,关于如何制作自己的静态库(Static Library)和动态库(Shared Library),可以进入这里查看。

但是,但是还用不着自己创建库。需要掌握的是,链接库的命令和如何添加路径。

二、文件树

  1. ├── CMakeLists.txt
  2. ├── include
  3. └── static
  4. └── Hello.h
  5. └── src
  6. ├── Hello.cpp
  7. └── main.cpp

2.1 Hello.h

  1. /* 声明了Hello类,Hello的方法是print() */
  2. #ifndef __HELLO_H__
  3. #define __HELLO_H__
  4. class Hello {
  5. public:
  6. void print();
  7. };
  8. #endif

2.2 Hello.cpp

  1. /* 实现了Hello::print() */
  2. #include <iostream>
  3. #include "static/Hello.h"
  4. void Hello::print() {
  5. std::cout << "Hello Static Library!" << std::endl;
  6. }

2.3 main.cpp

  1. #include "static/Hello.h"
  2. int main(int argc, char *argv[]) {
  3. Hello hi;
  4. hi.print();
  5. return 0;
  6. }

2.4 CMakeLists.txt

  1. cmake_minimum_required(VERSION 3.5)
  2. project(hello_library)
  3. # 1.创建一个静态库,库的源文件 Hello.cpp, 生成静态库 hello_library
  4. add_library(hello_library STATIC
  5. src/Hello.cpp)
  6. # 2.target_include_directories为一个目标(可能是一个库library
  7. # 或者是可执行文件)添加头文件路径
  8. target_include_directories(hello_library
  9. PUBLIC
  10. ${PROJECT_SOURCE_DIR}/include)
  11. # 3.创建可执行文件
  12. # 3.1 指定用哪个源文件生成可执行文件
  13. add_executable(hello_binary src/main.cpp)
  14. # 3.2 链接可执行文件和静态库
  15. target_link_libraries(hello_binary
  16. PRIVATE
  17. hello_library)

三、CMake命令解析

3.1 创建静态库

add_library() 函数用于从某些源文件创建一个库,默认生成在构建文件夹。 写法如下:

add_library(hello_library STATIC src/Hello.cpp)

add_library 调用中包含了源文件,用于创建名称为 libhello_library.a 的静态库。

注意:如前面的示例所述,将源文件直接传递给 add_library 调用,这是 modern CMake 的建议。(而不是先把Hello.cpp赋给一个变量)

3.2 添加头文件所在的目录

使用 target_include_directories() 添加了一个目录,这个目录是库所包含的头文件的目录,并设置库属性为 PUBLIC

target_include_directories(hello_library
                           PUBLIC
                           ${PROJECT_SOURCE_DIR}/include)

使用这个函数后,这个目录会在以下情况被调用:

  • 编译这个库的时候
    因为这个库 hello_library 由 Hello.cpp 生成,Hello.cpp 中函数的定义在 Hello.h 中,Hello.h 在这个 include目录下,所以显然编译这个库的时候,这个目录会用到
  • 编译链接到这个库 hello_library 的任何其他目标(库或者可执行文件)

3.2.1 private pubic interface的范围详解

大家如果去搜索,会发现解释杂乱无章。大部分解释是这样:

如果目标的头文件中包含了依赖的头文件(源文件间接包含),那么这里就是PUBLIC 如果目标仅源文件中包含了依赖的头文件,那么这里就是PRIVATE 如果目标的头文件包含依赖,但源文件未包含,那么这里就是INTERFACE

或者是这样:

当创建动态库时,

如果源文件(例如CPP)中包含第三方头文件,但是头文件(例如hpp)中不包含该第三方文件头,采用PRIVATE。 如果源文件和头文件中都包含该第三方文件头,采用PUBLIC。 如果头文件中包含该第三方文件头,但是源文件(例如CPP)中不包含,采用 INTERFACE。

我个人认为上面的说法是错误的。

正确理解:

  • PRIVATE - 目录被添加到目标(库)的包含路径中。
  • INTERFACE - 目录没有被添加到目标(库)的包含路径中,而是链接了这个库的其他目标(库或者可执行程序)包含路径中
  • PUBLIC - 目录既被添加到目标(库)的包含路径中,同时添加到了链接了这个库的其他目标(库或者可执行程序)的包含路径中

也就是说,根据库是否包含这个路径,以及调用了这个库的其他目标是否包含这个路径,可以分为三种scope。

建议在创建头文件的工程中,考虑到如下原则!

  • 对于公共的头文件,最好在 include 文件夹下建立子目录。
  • 传递给函数 **target_include_directories()**的目录,应该是所有包含目录的根目录,然后在这个根目录下建立不同的文件夹,分别写头文件。

这样使用的时候,不需要写 **${PROJECT_SOURCE_DIR}/include**,而是直接选择对应的文件夹里对应头文件。下面是例子:**#include "static/Hello.h"**而不是**#include "Hello.h"**使用此方法意味着在项目中使用多个库时,头文件名冲突的可能性较小。

3.3 链接库

创建将使用这个库的可执行文件时,必须告知编译器需要用到这个库。 可以使用 target_link_library() 函数完成此操作。**add_executable()** 连接源文件,**target_link_libraries()** 连接库文件

# main.cpp生成可执行文件hello_binary
add_executable(hello_binary src/main.cpp)

# 可执行文件hello_binary需要用到库hello_library
target_link_libraries( hello_binary PRIVATE hello_library)

这告诉 CMake 在链接期间将 hello_library 链接到 hello_binary 可执行文件。 同时,这个被链接的库如果有INTERFACE 或者 PUBLIC 属性的包含目录,那么,这个包含目录也会被传递( propagate )给这个可执行文件。

四、构建示例

$ mkdir build

$ cd build

$ cmake ..
-- The C compiler identification is GNU 8.4.1
-- The CXX compiler identification is GNU 8.4.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/CmakeTest/build


$ make
[ 25%] Building CXX object CMakeFiles/hello_library.dir/src/Hello.cpp.o
[ 50%] Linking CXX static library libhello_library.a
[ 50%] Built target hello_library
[ 75%] Building CXX object CMakeFiles/hello_binary.dir/src/main.cpp.o
[100%] Linking CXX executable hello_binary
[100%] Built target hello_binary


$ ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake  hello_binary  libhello_library.a  Makefile

$ ./hello_binary                # 可执行文件
Hello Static Library!