Skip to content

添加库和链接库

  • 在cmake中,如果要使用库,需要两步:添加库链接库

    • 添加库将源代码编译成库文件的形式
    • 链接库:在编译可执行文件的过程中,link库文件。
  • 在一个工程中,不可能把所有的源文件都写在一个目录下,通常是把不同功能的源文件放在不同的目录下,最外层只放置main.cppCMakeLists.txt还有一些其他配置文件。

    • cmake如何使用不同目录下面的源文件呢?
      • 将不同目录下面的源文件编译成库(添加库),在上层的CMakeLists.txt中链接这个库,就可以在上层源文件中调用子目录中的源文件。
  • 我们的文件结构如下所示:

    • ├── CMakeLists.txt
      ├── MathFunctions
      │   ├── CMakeLists.txt
      │   ├── MathFunctions.cpp
      │   ├── MathFunctions.h
      │   ├── mysqrt.cpp
      │   └── mysqrt.h
      ├── TestConfig.h.in
      └── test.cpp
    • 很显然,我们要在test.cpp中调用MathFunctions目录下面的源文件中的函数,因此我们需要把该目录下面的源文件编译成库文件

      • MathFunctions目录给出了一个典型库文件的写法,对外展示一个MathFunctions.cppMathFunctions.h文件,如果要扩充这个库(比如增加加法函数,我们叫做扩充这个库的细节),只需要增加myadd.cppmyadd.h,并且声明在MathFunctions.cppMathFunctions.h文件即可。
  • MathFunctions.h

    • cpp
      #ifndef __MATHFUNCTIONS_H__
      #define __MATHFUNCTIONS_H__
      #include "mysqrt.h"
      
      namespace mathfunctions
      {
          double sqrt(double x);
      } // namespace mathfunctions
      
      #endif
  • MathFunctions.cpp

    • cpp
      #include "MathFunctions.h"
      
      double mathfunctions::sqrt(double x)
      {
          return mathfunctions::details::mysqrt(x);
      }
  • mysqrt.h

    • cpp
      #ifndef __MYSQRT_H__
      #define __MYSQRT_H__
      namespace mathfunctions
      {
          namespace details
          {
              double mysqrt(double x);        
          } // namespace details
          
      } // namespace mathfunctions
      
      #endif
  • mysqrt.cpp

    • cpp
      #include "mysqrt.h"
      #include <iostream>
      double mathfunctions::details::mysqrt(double x)
      {
          if (x <= 0)
          {
              return 0;
          }
      
          double result = x;
      
          // do ten iterations
          for (int i = 0; i < 10; ++i)
          {
              if (result <= 0)
              {
                  result = 0.1;
              }
              double delta = x - (result * result);
              result = result + 0.5 * delta / result;
          }
          return result;
      }
  • 对于上述源文件和头文件,我们的想法是编译成一个库:MathFunctions

    • 有两种思路:

      • 掺杂编译:
        • 将所有的源文件混在一起编译,最终形成一个库。
      • 分开编译:
        • mysqrt.cpp编译成一个库:MathMysqrt,然后将MathMysqrt以及MathFunctions.cpp编译成库:MathFunctions
    • 这里我们选择分开编译

      • 首先,构建MathMysqrt库

        • cmake
          add_library(MathMysqrt mysqrt.cpp)
          #添加MathMysqrt库,也就是编译这个源文件,生成库文件
      • 接着,我们构建MathFunctions库

        • cmake
          add_library(MathFunctions MathFunctions.cpp)
          #添加MathFunctions库,也就是编译这个源文件,生成库文件
          
          add_library(MathMysqrt mysqrt.cpp)
          
          target_link_libraries(MathFunctions PRIVATE MathMysqrt)
          #编译MathFunctions的过程中,链接MathMysqrt库文件,这个链接过程设置为私有
      • 最后,在上层CMakeLists.txt文件中调用MathFunctions库文件:

        • 这里,我们要给上层调用者一个库的接口,通常,库所对应的头文件就是库的接口,即MathFunctions.h

        • cmake
          cmake_minimum_required(VERSION 3.10)
          
          project(Test VERSION 1.0)
          
          set(CMAKE_CXX_STANDARD 11)
          set(CMAKE_CXX_STANDARD_REQUIRED True)
          
          configure_file(TestConfig.h.in TestConfig.h)
          
          add_subdirectory(MathFunctions)
          
          add_executable(Test test.cpp)
          
          target_link_libraries(Test PUBLIC MathFunctions)
          
          target_include_directories(Test PUBLIC 
                                     "${PROJECT_BINARY_DIR}"
                                     "${PROJECT_SOURCE_DIR}/MathFunctions"
          )
        • 关于add_subdirectory(MathFunctions)

          • 给出要调用的库的路径,实际上会执行该路径下的CMakeLists.txt,也就是编译出要使用的库文件。
        • 关于target_link_libraries(Test PUBLIC MathFunctions)

          • 在构建Test可执行文件的时候,公开链接MathFunctions这个库。
        • 关于target_include_directories,要找到MathFunctions库对外的接口头文件

      • test.cpp

        • cpp
          #include <iostream>
          #include "TestConfig.h"
          #include "MathFunctions.h"
          int main(int argc, char *argv[])
          {
              if (argc == 1)
              {
                  std::cout << "Hello World" << std::endl;
              }
              else if (argc == 2)
              {
                  std::string argv_1 = argv[1];
                  if (argv_1 == "--version")
                  {
                      std::cout << " Version " << Test_VERSION_MAJOR << "." << Test_VERSION_MINOR << std::endl;
                  }
                  else 
                  {
                      double x = std::stod(argv_1);
                      std::cout << argv_1 << " sqrt is: " << mathfunctions::sqrt(x) << std::endl; 
                  }
              }
              
          }
      • 构建信息:

        • shell
          [ 16%] Building CXX object MathFunctions/CMakeFiles/MathMysqrt.dir/mysqrt.cpp.o
          [ 33%] Linking CXX static library libMathMysqrt.a
          [ 33%] Built target MathMysqrt
          [ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.cpp.o
          [ 66%] Linking CXX static library libMathFunctions.a
          [ 66%] Built target MathFunctions
          Consolidate compiler generated dependencies of target Test
          [ 83%] Building CXX object CMakeFiles/Test.dir/test.cpp.o
          [100%] Linking CXX executable Test
          [100%] Built target Test
          • 可以看到,在构建Test可执行文件之前,先构建了MathFunctions库,在这之前还构建了MathMysqrt库
      • 执行结果:

        • ./Test 10

        • shell
          10 sqrt is: 3.16228
  • 如果我们有权限选择MathFunctions中的sqrt函数调用MathMysqrt中的mysqrt或者标准库中的sqrt,那么我们就需要在MathFunctions中以及MathFunctions对应的CMakeLists.txt中都设置条件,以符合条件编译

    • 当我们需要调用MathMysqrt中的mysqrt时,就需要编译MathMysqrt这个库文件。
    • 当我们需要调用标准库中的sqrt时,就不需要编译MathMysqrt这个库文件。
  • CMakeLists.txt

    • cmake
      add_library(MathFunctions MathFunctions.cpp)
      option(USE_MYMATH "Use my sqrt functions" ON)
      
      if (USE_MYMATH)
      target_compile_definitions(MathFunctions PRIVATE "USE MYMATH")
      
      add_library(MathMysqrt mysqrt.cpp)
      
      target_link_libraries(MathFunctions PRIVATE MathMysqrt)
      endif()
    • 关于option

      • 使用option命令创建了一个变量(通常叫做编译定义)USE_MYMATH,这个变量被配置为ON
    • 关于target_compile_definitions

      • USE_MYMATH这个编译定义传递到MathFunctions库中(相当于**#define声明宏**)。
    • 如果USE_MYMATH这个编译定义被设置,那么将会编译MathMysqrt库,同时MathFunctions库MathMysqrt库构建。

  • MathFunctions.cpp

    • cpp
      #include "MathFunctions.h"
      
      double mathfunctions::sqrt(double x)
      {
      #ifdef USE_MYMATH
          return mathfunctions::details::mysqrt(x);
      #else
          return std::sqrt(x);
      #endif
      }
  • MathFunctions.h

    • cpp
      #ifndef __MATHFUNCTIONS_H__
      #define __MATHFUNCTIONS_H__
      
      #ifdef USE_MYMATH
      #include "mysqrt.h"
      #else
      #include <cmath>
      #endif
      
      namespace mathfunctions
      {
          double sqrt(double x);
      } // namespace mathfunctions
      
      #endif
  • 开启USE_MYMATH编译构建:

    • shell
      [ 33%] Built target MathMysqrt
      Consolidate compiler generated dependencies of target MathFunctions
      [ 66%] Built target MathFunctions
      Consolidate compiler generated dependencies of target Test
      [100%] Built target Test
      • 可以发现,此时编译了MathMysqrt库文件
  • 关闭USE_MYMATH编译构建:

    • shell
      Consolidate compiler generated dependencies of target MathFunctions
      [ 50%] Built target MathFunctions
      Consolidate compiler generated dependencies of target Test
      [100%] Built target Test
      • 可以发现,此时没有编译MathMysqrt库文件。

Released under the MIT License.