第二步,添加一个库
相关代码: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
控制的是同名宏的定义与否,而非宏值。