第二步 引用一个库
这个真的是很小白,很入门了,当然也是因为我菜,不写的透彻一点,以后我也会看不懂。
官方提供了初始的代码,方便学习的人改动。
初始代码
构建库
这个例子是源码提供的库,那么当然需要指定编译和链接两个步骤。
首先在MathFunctions子文件夹中,建立一个一行的CMakeLists.txt文件,里面加入一行
add_library(MathFunctions mysqrt.cxx)
它的申明是这样的
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
我们先试着看一下这个add_library()命令是干什么的,在这个目录下输入
mkdir build;cd build;cmake ..
这个时候会在build文件夹下面生成一个CMakeFiles的文件夹,里面又有一个MathFunctions.dir的文件夹,里面可以找到mysqrt.o,没错,这个“cmake”编译出的二进制文件,这个时候回到build文件夹,里面应该有一个Makefile文件,执行
make
你就会发现libMathFunctions.a这个静态库文件了,它就是刚才那个.o文件打包来的,当然不同平台文件名也应该不一样,在Linux上会显示libxxx.a,windows上就会是xxx.lib。
所以,这个命令的作用就是通过指定的[source]按照指定的方式[STATIC | SHARED | MODULE]来生成对应的库<name>,EXCLUDE_FROM_ALL这个参数是用来排除不想编译的文件的,先不考虑。
当然,这是个静态库,根据上面的函数申明,除此之外还可以编译成动态库,只需要改成
add_library(MathFunctions SHARED mysqrt.cxx)
这样按照刚才的步骤来,生成就会是libxxx.so文件了,而MODULE这个选项也是生成的动态库的,但一般都是用于运行时链接多用于类似于dlopen这样的函数来加载打开。
引用库
回到第二步的顶层文件夹,在CmakeLists.txt文件中的add_executable()命令前面加上
add_subdirectory(MathFunctions)
当cmake运行到这个命令时,就会引用MathFunctions中的CMakeList.txt文件了,这意味着当构建项目时,刚才生成的库不会再生成在MathFunctions这个文件夹下了,而是会生成在顶层CMakeLists.txt的构建文件夹下面。
到目前为止,还没有链接到生成的库文件,现在在add_executable()下面加入
target_link_libraries(Tutorial PUBLIC MathFunctions)
这个时候就相当于把生成的库文件加入编译选项了,但是还没有指定头文件路径,修改把target_include_directories()命令
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
这样,就指定头文件的路径了。
__这样就可以了?__当然不行,看一下tutorial.cxx,因为这个时候用的压根不是我们刚才编译的库,而是编译器提供的库,现在我们来指定用哪个库。
加入构建选项
如果有使用过cmake编译一些类似opencv的同学注意到,在用cmake-gui构建的时候,会有很多构建选项,我们也可以提供构建选项,来决定最终编译的时候用哪个库。
在顶层的CMakeLists.txt文件中的configure_file()命令上面加入
option(USE_MYMATH "Use tutorial provided math implementation" ON)
这个命令创建了一个叫做USE_MYMATH的宏,默认值是ON
这个时候就可以在使用cmake-gui的时候看到这个选项了,但实际上,这个宏还没有用,我们来使用一下这个宏。
把之前的那句add_subdirectory()去掉,然后把代码改为
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
这个if()、endif()和C语言的预编译选项很类似了,只不过加了个括号。
list(APPEND EXTRA_LIBS MathFunctions)
这个命令我的理解是,LIST()会在当前的CMake作用域中创建一个叫做MathFunctions的变量加入EXTRA_LIBS这个列表。具体的使用方式和说明在这里
LIST的使用
使用这种方式,就不用每添加一个库就写一个target_link_libraries和target_include_directories了。
接下来修改一下源文件
在tutorial.cxx的开头中加入
#ifdef USE_MYMATH
# include "MathFunctions.h"
#endif
然后修改平方根的那行代码为
#ifdef USE_MYMATH
const double outputValue = mysqrt(inputValue);
#else
const double outputValue = sqrt(inputValue);
#endif
然后在TutorialConfig.h.in中加入(这个文件是构建时生成头文件用的,具体看上一步)
#cmakedefine USE_MYMATH
这些改动是为了把构建期的行为,传递到运行期
接下来再构建一下这个项目,编译,OJBK
官方最后留了一个问题如果
configure_file(TutorialConfig.h.in TutorialConfig.h)
这个命令出现在
option(USE_MYMATH "Use tutorial provided math implementation" ON)
前面,会发生什么。
答案是在最终生成的TutorialConfig.h中并不会生成USE_MYMATH这个宏,而是
/* #undef USE_MYMATH */