CMake教程讀書筆記-第二步,添加一個庫 第二步,添加一個庫

第二步,添加一個庫

相關代碼:2_addlib

現在,我們向項目中添加一個自己實現的庫(即 MathFunctions)。這個庫包含一個我們自己實現的、計算一個數平方根的函數(即 mysqrt),可執行文件可以使用這個庫函數來替代編譯時提供的標準平方根庫函數(即 sqrt)。

添加一個庫到項目中,會涉及到:增加模塊開關(注意cmake的變量和代碼的宏似乎可以不同名)、爲項目添加邏輯模塊、包含接口文件、添加編譯子目錄、添加鏈接選項等,下面會詳細舉例說明。

自定義庫的 MathFunctions/CMakeLists.txt

這裏,我們將庫放置到 MathFunctions 子目錄中。然後在其 CMakeLists.txt 中(注意,是 MathFunctionsCMakeLists )加入這樣一行:

add_library(MathFunctions mysqrt.cxx)

MathFunctions/mysqrt.cxx

源文件 mysqrt.cxx 定義了一個叫做 mysqrt 的函數,這個函數功能和之前編譯提供的 sqrt 類似。

#include <stdio.h>
double mysqrt(int n)
{
        fprintf(stderr, "custom square root, not implementated currently.\n");
        return 0;
}

MathFunctions/MathFunctions.h

頭文件定義在源文件目錄中,稍後會添加到包含路徑。內容如下:

#ifndef __math_functions_h__
#define __math_functions_h__
double mysqrt(int n);
#endif

頂級 CMakeLists.txt

爲了能夠使用新實現的庫,我們在頂級 CMakeLists.txt 中添加 add_subdirectory 調用,這樣這個庫就能夠被編譯到。

同時我們也添加了另外一個包含目錄(即 ${PROJECT_SOURCE_DIR}/MathFunctions),以便聲明瞭函數的 MathFunctions/mysqrt.h 頭文件能夠被找到。

最後添加新庫鏈接到 executable 中(即 target_link_libraries ),這樣頂級 CMakeLists.txt 文件的最後幾行看起來是這個樣子:

...skip above lines...
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions) 
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)

MathFunctions 設置爲可選

接下來,我們看看如何讓 MathFunctions 庫變得可選。這對於大型庫或者依賴第三方代碼的庫,很有必要。

首先,在頂級 CMakeLists.txt 文件中添加一個選項:

# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON)

這會交互顯示一個用戶可以改變的 CMake 選項(在使用 ccmake 生成 Makefile ,可以看到一個圖形交互界面; 使用 cmake -i 可以根據交互提示設定選項),選項的默認值爲 ON 。 這個設置將會保存在緩存中,這樣用戶不用在每次爲這個項目運行 CMake 的時候必須選擇設置它們。

下面爲使 MathFunctions 庫的編譯和鏈接依照選項對應的特定條件進行,我們將頂級 CMakeLists.txt 文件的的末尾修改成這個樣子:

# add the MathFunctions library?
#
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})

這裏,使用 USE_MYMATH 的設置來確定 MathFunctions 是否應當被編譯和使用。這裏需要注意,爲了收集按特定條件將被鏈接到可執行文件的可選庫,我們使用了一個變量(這裏是指 EXTRA_LIBS )。這也是用來維護具有許多選項組件的大項目的常用方法。

tutorial.cxx

相應的,我們直接修改代碼如下:

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif

int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n", argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }

  double inputValue = atof(argv[1]);

#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
#else
  double outputValue = sqrt(inputValue);
#endif

  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

TutorialConfig.h.in

在前面代碼中,我們使用了 USE_MYMATH ,但是這個宏在源代碼的角度上,卻不是直接就存在的。

通過向 TutorialConfig.h.in 加入下面行,可以讓 CMake 藉助 TutorialConfig.h.in 配置文件,讓我們可以在代碼中使用 USE_MYMATH 宏。

#cmakedefine USE_MYMATH

詳細過程

下面通過時間回顧一下過程。

  1. 原始文件目錄結構

    $ls -p
    tree.cmakelog  tree.cleanlog  tree.makelog  tree.origin  tutorial/
    
    $tree . >tree.origin
    
    $cat tree.origin
    .
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    └── tutorial
        ├── CMakeLists.txt
        ├── MathFunctions
        │   ├── CMakeLists.txt
        │   ├── MathFunctions.h
        │   └── mysqrt.cpp
        ├── TutorialConfig.h.in
        └── tutorial.cpp
    
    2 directories, 10 files
    

    這裏, tree.cmakelog, 與 tree.cleanlog, tree.makelog, tree.origin 等都是用於記錄輸出日誌的文件。

  2. cmake -i 通過選項的交互選擇生成 Makefile

    $cmake -i tutorial/
    Would you like to see advanced options? [No]:
    Please wait while cmake processes CMakeLists.txt files....
    
    Variable Name: CMAKE_BUILD_TYPE
    Description: Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
    Current Value: 
    New Value (Enter to keep current value): 
    
    Variable Name: CMAKE_INSTALL_PREFIX
    Description: Install path prefix, prepended onto install directories.
    Current Value: /usr/local
    New Value (Enter to keep current value): 
    
    Variable Name: USE_MYMATH
    Description: Use tutorial provided math implementation
    Current Value: ON
    New Value (Enter to keep current value): 
    
    Please wait while cmake processes CMakeLists.txt files....
    
    CMake complete, run make to build project.
    
    $tree . >tree.cmakelog
    $cat tree.cmakelog
    .
    ├── CMakeCache.txt
    ├── CMakeFiles
    │   ├── CMakeCCompiler.cmake
    │   ├── cmake.check_cache
    │   ├── CMakeCXXCompiler.cmake
    │   ├── CMakeDetermineCompilerABI_C.bin
    │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   ├── CMakeDirectoryInformation.cmake
    │   ├── CMakeOutput.log
    │   ├── CMakeSystem.cmake
    │   ├── CMakeTmp
    │   │   └── CMakeFiles
    │   │       └── cmTryCompileExec.dir
    │   ├── CompilerIdC
    │   │   ├── a.out
    │   │   └── CMakeCCompilerId.c
    │   ├── CompilerIdCXX
    │   │   ├── a.out
    │   │   └── CMakeCXXCompilerId.cpp
    │   ├── Makefile2
    │   ├── Makefile.cmake
    │   ├── progress.marks
    │   ├── TargetDirectories.txt
    │   └── Tutorial.dir
    │       ├── build.make
    │       ├── cmake_clean.cmake
    │       ├── DependInfo.cmake
    │       ├── depend.make
    │       ├── flags.make
    │       ├── link.txt
    │       └── progress.make
    ├── cmake_install.cmake
    ├── Makefile
    ├── MathFunctions
    │   ├── CMakeFiles
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── MathFunctions.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── cmake_clean_target.cmake
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   └── progress.make
    │   │   └── progress.marks
    │   ├── cmake_install.cmake
    │   └── Makefile
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    ├── tutorial
    │   ├── CMakeLists.txt
    │   ├── MathFunctions
    │   │   ├── CMakeLists.txt
    │   │   ├── MathFunctions.h
    │   │   └── mysqrt.cpp
    │   ├── TutorialConfig.h.in
    │   └── tutorial.cpp
    └── TutorialConfig.h
    
    12 directories, 49 files
    

    這裏,也可通過 ccmake 圖形方式交互生成對應選項的 Makefile, 或者 cmake -DUSE_MYMATH=ON 。另外,直接運行 cmake 並沒有像 CMakeLists.txt 那樣默認使用 USE_MYMATH=ON , 但是,再次運行 cmake 則會發現啓用了該選項。 這個option命令和你本地是否存在編譯緩存的關係很大。

    源碼目錄 tutorial 仍舊不變,頭文件 MathFunctions.h 也仍舊在源碼目錄中。

  3. make 編譯

    $make
    Scanning dependencies of target MathFunctions
    [ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cpp.o
    Linking CXX static library libMathFunctions.a
    [ 50%] Built target MathFunctions
    Scanning dependencies of target Tutorial
    [100%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cpp.o
    Linking CXX executable Tutorial
    [100%] Built target Tutorial
    
    $tree . >tree.makelog
    $cat tree.makelog
    .
    ├── CMakeCache.txt
    ├── CMakeFiles
    │   ├── CMakeCCompiler.cmake
    │   ├── cmake.check_cache
    │   ├── CMakeCXXCompiler.cmake
    │   ├── CMakeDetermineCompilerABI_C.bin
    │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   ├── CMakeDirectoryInformation.cmake
    │   ├── CMakeOutput.log
    │   ├── CMakeSystem.cmake
    │   ├── CMakeTmp
    │   │   └── CMakeFiles
    │   │       └── cmTryCompileExec.dir
    │   ├── CompilerIdC
    │   │   ├── a.out
    │   │   └── CMakeCCompilerId.c
    │   ├── CompilerIdCXX
    │   │   ├── a.out
    │   │   └── CMakeCXXCompilerId.cpp
    │   ├── Makefile2
    │   ├── Makefile.cmake
    │   ├── progress.marks
    │   ├── TargetDirectories.txt
    │   └── Tutorial.dir
    │       ├── build.make
    │       ├── cmake_clean.cmake
    │       ├── CXX.includecache
    │       ├── DependInfo.cmake
    │       ├── depend.internal
    │       ├── depend.make
    │       ├── flags.make
    │       ├── link.txt
    │       ├── progress.make
    │       └── tutorial.cpp.o
    ├── cmake_install.cmake
    ├── Makefile
    ├── MathFunctions
    │   ├── CMakeFiles
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── MathFunctions.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── cmake_clean_target.cmake
    │   │   │   ├── CXX.includecache
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.internal
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   ├── mysqrt.cpp.o
    │   │   │   └── progress.make
    │   │   └── progress.marks
    │   ├── cmake_install.cmake
    │   ├── libMathFunctions.a
    │   └── Makefile
    ├── tree.cleanlog
    ├── tree.cmakelog
    ├── tree.makelog
    ├── tree.origin
    ├── tutorial
    │   ├── CMakeLists.txt
    │   ├── MathFunctions
    │   │   ├── CMakeLists.txt
    │   │   ├── MathFunctions.h
    │   │   └── mysqrt.cpp
    │   ├── TutorialConfig.h.in
    │   └── tutorial.cpp
    ├── Tutorial
    └── TutorialConfig.h
    
    12 directories, 57 files
    

更多信息

通常過程

代碼如下

$ tree .
.
└── tutorial
    ├── CMakeLists.txt
    ├── MathFunctions
    │   ├── CMakeLists.txt
    │   ├── MathFunctions.h
    │   └── mysqrt.cpp
    ├── TutorialConfig.h.in
    └── tutorial.cpp

2 directories, 6 files

$ mkdir build_on && build_on
$ cmake -DUSE_MYMATH=ON ../tutorial
$ make
$ ./Tutorial 
./Tutorial Version 1.0
Usage: ./Tutorial number
$ ./Tutorial 25
custom square root.
The square root of 25 is 0

關於添加選項

必須在 configure_file 之前

注意:添加的選項必須在 configure_file 配置前面,如下:

......
# should we use our own math functions?                                                                                                                  option (USE_MYMATH "Use tutorial provided math implementation" ON) 

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
    ) 
......

否則,option將不起作用,比如:

......
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
    ) 

# should we use our own math functions?                                                                                                                  option (USE_MYMATH "Use tutorial provided math implementation" ON) 
......

關於 xxxconfig.h.in 中的 #cmakedefine#define, 和 CMakefileLists.txt 中的 add_definitions()

根據 [[https://cmake.org/pipermail/cmake/2011-March/043464.html][#cmakedefine vs #define]]CMAKE 中 add_definitions的用法 可知,

  • config.h.in 中用 #cmakedefine 定義的宏 VAR ,會根據 CMakeLists.txt 中是否有 VAR 變量,在 config.h 中進行 #define VAR#undefine VAR
  • config.h.in 中用 #define 定義的宏 VAR ,使用 @VAR@ 引用 CMakeLists.txtVAR 變量的值,會直接在 config.h#define VAR
  • CMakeLists.txt 中使用 add_definitions(-DVAR), 其實是直接對編譯器添加 -DVAR 選項(如果不關心規範cmake可藉此加入其它編譯選項而非僅僅 -D ),導致代碼有 #define VAR 的效果。

其它的方法

除了前面的方法,實際上,更方便添加選項的方法還有:

添加如下內容到 CMakeLists.txt

project(Tutorial)
     
option(USE_MYMATH "Use tutorial provided math implementation" OFF)
if (USE_MYMATH)
add_definitions(-DTEST_DEBUG)
endif()

源代碼宏

#ifdef USE_MYMATH
......
#endif

編譯打開選項

$cmake -DUSE_MYMATH=ON tutorial
$cmake --build tutorial

這樣不用寫 TutorialConfig.h.in 文件了。 opt 對應的是變量,其值裏面的 ON/OFF 控制的是同名宏的定義與否,而非宏值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章