步骤2:添加库(Step 2: Adding a Library)
我们已经学习了如何使用CMake创建一个基本的项目了。本步骤,我们学习如何在项目中创建和使用库,以及如何设置使用/不使用库。
练习1:创建库(Exercise 1 - Creating a Library)
在CMake中,使用命令add_library()、指定组成库的源文件来添加库。
在项目中,一般不会把所有源文件都放在一个文件夹下,而是使用多个子文件。创建一个子文件夹,用来存放库,在此文件夹下,创建一个新的CMakeLists.txt
文件及源文件。在顶层的CMakeLists.txt
文件中,使用命令add_subdirectory(),把子文件夹添加到生成中。
库创建后,使用命令target_include_directories()和target_link_libraries()将其与可执行目标文件关联起来。
目标(Goal)
添加、使用库。
参考资料(Helpful Resources)
add_library()
add_subdirectory()
target_include_directories()
target_link_libraries()
PROJECT_SOURCE_DIR
需编辑的文件(Files to Edit)
CMakeLists.txt
tutorial.cxx
MathFunctions/CMakeLists.txt
开始(Getting Started)
本练习,添加计算数的平方根的库到项目中。可执行文件使用这个库而不是编译器提供的库来计算平方根。
将库放到MathFunctions
子文件下,此文件夹下已有头文件MathFunctions.h
和源文件mysqrt.cxx
,不需要修改这两个文件。源文件中有一个名为mysqrt
的函数,其功能与编译器的sqrt
函数相似。
在Help/guide/tutorial/Step2
下,完成TODO 1
到TODO 6
。
首先,在子文件夹MathFunctions
下的CMakeLists.txt
文件中添加一行。
然后,编辑顶层CMakeLists.txt
文件。
最后,在tutorial.cxx
中使用新创建的MathFunctions
库。
练习答案(Solution)
在文件夹MathFunctions
下的CMakeLists.txt
文件中,使用add_library()
创建一个名为MathFunctions
的库目标。库的源文件作为add_library()
的参数:
-
TODO 1
,MathFunctions/CMakeLists.txt
:
add_library(MathFunctions mysqrt.cxx)
为了使用新的库,在顶层CMakeLists.txt
文件中,使用命令add_subdirectory()
,以生成库。
-
TODO 2
,CMakeLists.txt
:
add_subdirectory(MathFunctions)
然后,使用target_link_libraries()
把库目标链接到可执行目标上。
-
TODO 3
,CMakeLists.txt
:
target_link_libraries(Tutorial PUBLIC MathFunctions)
最后,需要指定库的头文件位置。修改target_include_directories()
添加MathFunctions
为包含目录以找到MathFunctions.h
头文件。
-
TODO 4
,CMakeLists.txt
:target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions" )
现在就可以使用库了。在tutorial.cxx
,包含MathFunctions.h
:
-
TODO 5
,tutorial.cxx
:
#include "MathFunctions.h"
最后,使用mysqrt
替换sqrt
:
-
TODO 6
,tutorial.cxx
:
const double outputValue = mysqrt(inputValue);
生成运行(Build and Run)
在命令行中运行:
mkdir Step2_build
cd Step2_build
cmake ../Step2
cmake --build .
运行新生成的Tutorial
,是否能求出正确的平方根。
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source> mkdir Step2_build
目录: D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/11/2023 11:16 PM Step2_build
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source> cd .\Step2_build\
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake ../Step2
-- Building for: Visual Studio 17 2022
-- The C compiler identification is MSVC 19.35.32215.0
-- The CXX compiler identification is MSVC 19.35.32215.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.35.32215/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.35.32215/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (193.0s)
-- Generating done (0.4s)
-- Build files have been written to: D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
MSBuild version 17.5.0+6f08c67f3 for .NET Framework
Checking Build System
Building Custom Rule D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/MathFunctions/CMakeLists.txt
mysqrt.cxx
MathFuctions.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\MathFunctions\Debug\Math
Fuctions.lib
Building Custom Rule D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/CMakeLists.txt
tutorial.cxx
LINK : fatal error LNK1104: 无法打开文件“MathFunctions.lib” [D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_b
uild\Tutorial.vcxproj]
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
CMake is re-running because D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/MathFunctions/CMakeFiles/generate.stamp is out-of-date.
the file 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/MathFunctions/CMakeLists.txt'
is newer than 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/MathFunctions/CMakeFiles/generate.stamp.depend'
result='-1'
-- Configuring done (0.0s)
-- Generating done (0.2s)
-- Build files have been written to: D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build
MSBuild version 17.5.0+6f08c67f3 for .NET Framework
Checking Build System
Building Custom Rule D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/MathFunctions/CMakeLists.txt
mysqrt.cxx
MathFunctions.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\MathFunctions\Debug\Mat
hFunctions.lib
Building Custom Rule D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/CMakeLists.txt
Tutorial.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug\Tutorial.exe
Building Custom Rule D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/CMakeLists.txt
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cd .\Debug\
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> .\Tutorial.exe 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
运行中出现错误提示,无法打开文件MathFunctions.lib
,是因为在:
-
TODO 1
,MathFunctions/CMakeLists.txt
:
add_library(MathFunctions mysqrt.cxx)
写错了生成的库名,写成了MathFuctions
,少了个n
,所以出现了上述错误。
练习2:使库可选(Exercise 2 - Making Our Library Optional)
虽然在教程中不需要选择是否使用库,但是在大型项目中,经常有这种需求。
CMake使用命令option()
完成此项功能,会提供一个变量给用户,用户在配置cmake生成时可改变此变量值。此设置存在缓存中,因此,用户不需要在每次运行CMake时,都设置此值。
目标(Goal)
添加选项,以在生成时是否使用MathFunctions
。
参考资料(Helpful Resources)
- if()
- list()
- option()
- cmakedefine
需编辑的文件(Files to Edit)
- CMakeLists.txt
- tutorial.cxx
- TutorialConfig.h.in
开始(Getting Started)
从练习1的结果开始,完成TODO 7
到TODO 13
。
首先,在顶层CMakeLists.txt
文件中使用命令option()
创建变量USE_MYMATH
,并使用此变量决定是否在生成中使用MathFunctions
库。
然后,更新文件tutorial.cxx
和TutorialConfig.h.in
以使用USE_MYMATH
。
答案(Solution)
第一步,在顶层CMakeLists.txt
文件中添加选项,此选项在cmake-gui
和ccmake
中默认值为ON
,用户可更改。
-
TODO 7
,CMakeLists.txt
:
option(USE_MYMATH "Use tutorial provided math implementation" ON)
然后,建立生成、链接MathFunctions
的条件。
首先,在项目中创建可选库的list()
,并命名为EXTRA_LIBS
,目前列表中仅有MathFunctions
。
同样,创建包含文件的list()
,命名为EXTRA_INCLUDES
,在此列表中APPEND
库头文件的路径。
然后,创建语句if()
判断USE_MYMATH
的值,在if()
语句块中,添加练习1中的命令add_subdirectory()
,和命令list()
。
当USE_MYMATH
是ON
时,会生成列表并添加到项目中,当USE_MYMATH
是OFF
时,列表为空。通过这种方式,用户可以使用USE_MYMATH
控制在生成中是否使用哪些库。
顶层的CMakeLists.txt
文件,添加内容:
-
TODO 8
,CMakeLists.txt
:if(USE_MYMATH) add_subdirectory(MathFunctions) list(APPEND EXTRA_LIBS MathFunctions) list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") endif()
现在有了两个列表,需要更新target_link_libraries()
和target_include_directories()
来使用这两个列表。
-
TODO 9
,CMakeLists.txt
:target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
-
TODO 10
,CMakeLists.txt
:target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES} )
这是处理多个部分的经典做法。在步骤3(Step 3)中会介绍现代做法。
源代码中也需要相应的改变。在文件tutorial.cxx
中,如果定义了USE_MYMATH
则包含MathFunctions.h
头文件。
-
TODO 11
,tutorial.cxx
:#ifdef USE_MYMATH # include "MathFunctions.h" #endif
同样,使用USE_MYMATH
开控制使用哪个平方根函数:
-
TODO 12
,tutorial.cxx
:#ifdef USE_MYMATH const double outputValue = mysqrt(inputValue); #else const double outputValue = sqrt(inputValue); #endif
由于源代码需要使用USE_MYMATH
,需要把它添加到TutorialConfig.h.in
文件中:
-
TODO 13
,TutorialConfig.h.in
:#cmakedefine USE_MYMATH
如此,就可以在生成或使用过程中来决定是否选用库文件了。
疑问
为啥需要在option USE_MYMATH
后,再配置TutorialConfig.h.in
,颠倒顺序会怎么样?
解答
因为配置TutorialConfig.h.in
时,使用了USE_MYMATH
的值,如果先于option()
进行配置,则无法使用USE_MYMATH
正确的值。
生成运行(Build and Run)
cd ../Step2_build
cmake --build .
然后,运行可执行文件Tutorial
,以验证程序的正确性。
改变USE_MYMATH
值为OFF
。最简单的方法是使用cmke-gui
或在终端(terminal)中使用ccmake
。另外,也可在命令行中运行:
cmake ../Step2 -D USE_MYMATH=OFF
然后,重新生成代码:
cmake --build .
再次运行可执行文件,确保其在USE_MYMATH
值为OFF
时仍可正确工作。哪个函数的结果更好,sqrt
还是mysqrt
?
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> cd ..
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
CMake is re-running because D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/CMakeFiles/generate.stamp is out-of-date.
the file 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/CMakeLists.txt'
is newer than 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/CMakeFiles/generate.stamp.depend'
result='-1'
CMake Error at CMakeLists.txt:31 (add_subdirectory):
The binary directory
D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/MathFunctions
is already used to build a source directory. It cannot be used to build
source directory
D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/MathFunctions
Specify a unique binary directory name.
-- Configuring incomplete, errors occurred!
CMake Configure step failed. Build files cannot be regenerated correctly.
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
CMake is re-running because D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/CMakeFiles/generate.stamp is out-of-date.
the file 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2/CMakeLists.txt'
is newer than 'D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build/CMakeFiles/generate.stamp.depend'
result='-1'
-- Configuring done (0.0s)
-- Generating done (0.1s)
-- Build files have been written to: D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build
MSBuild version 17.5.0+6f08c67f3 for .NET Framework
MathFunctions.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\MathFunctions\Debug\Mat
hFunctions.lib
tutorial.cxx
D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2\tutorial.cxx(20,44): error C2065: “Tutorial_VERSION_MAJO
R”: 未声明的标识符 [D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Tutorial.vcxproj]
D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2\tutorial.cxx(21,18): error C2065: “Tutorial_VERSION_MINO
R”: 未声明的标识符 [D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Tutorial.vcxproj]
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
MSBuild version 17.5.0+6f08c67f3 for .NET Framework
MathFunctions.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\MathFunctions\Debug\Mat
hFunctions.lib
tutorial.cxx
Tutorial.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug\Tutorial.exe
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cd .\Debug\
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> .\Tutorial.exe 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> cd ..
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake ../Step2 -D USE_MYMATH=OFF
-- Configuring done (0.1s)
-- Generating done (0.2s)
-- Build files have been written to: D:/VisualStudioProjects/cmake-3.26.0-rc6-tutorial-source/Step2_build
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cmake --build .
MSBuild version 17.5.0+6f08c67f3 for .NET Framework
Checking Build System
tutorial.cxx
Tutorial.vcxproj -> D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug\Tutorial.exe
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build> cd .\Debug\
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> .\Tutorial.exe 10
The square root of 10 is 3.16228
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> .\Tutorial.exe 100
The square root of 100 is 10
PS D:\VisualStudioProjects\cmake-3.26.0-rc6-tutorial-source\Step2_build\Debug> .\Tutorial.exe 1000
The square root of 1000 is 31.6228
第一个CMakeLists.txt
文件错误是,忘了注释掉:
# TODO 2: Use add_subdirectory() to add MathFunctions to this project
add_subdirectory(MathFunctions)
第二个错误未声明的标识符....
原因是,错误的注释掉了//#include "TutorialConfig.h"
,而是应该注释掉:
// TODO 5: Include MathFunctions.h
//#include "MathFunctions.h"