第二步,添加一個庫
相關代碼:2_addlib
現在,我們向項目中添加一個自己實現的庫(即 MathFunctions
)。這個庫包含一個我們自己實現的、計算一個數平方根的函數(即 mysqrt
),可執行文件可以使用這個庫函數來替代編譯時提供的標準平方根庫函數(即 sqrt
)。
添加一個庫到項目中,會涉及到:增加模塊開關(注意cmake的變量和代碼的宏似乎可以不同名)、爲項目添加邏輯模塊、包含接口文件、添加編譯子目錄、添加鏈接選項等,下面會詳細舉例說明。
自定義庫的 MathFunctions/CMakeLists.txt
這裏,我們將庫放置到 MathFunctions
子目錄中。然後在其 CMakeLists.txt
中(注意,是 MathFunctions
的 CMakeLists
)加入這樣一行:
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
詳細過程
下面通過時間回顧一下過程。
-
原始文件目錄結構
$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
等都是用於記錄輸出日誌的文件。 -
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
也仍舊在源碼目錄中。 -
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.txt
中VAR
變量的值,會直接在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
控制的是同名宏的定義與否,而非宏值。