CMake Tutorial 巡礼(6)_ 添加自定义命令并生成文件
这是本系列的第七篇。
上一篇我们学习了如何添加系统自察。这一篇我们来学习如何添加自定义命令并生成文件。
本章导读
第六步 添加自定义命令并生成文件
Suppose, for the purpose of this tutorial, we decide that we never want to use the platform
log
andexp
functions and instead would like to generate a table of precomputed values to use in themysqrt
function. In this section, we will create the table as part of the build process, and then compile that table into our application.
出于本教程的目的,假如我们决定,不使用平台提供的log
和exp
函数,而是生成一个预计算好的表,来用于mysqrt
函数的计算。在本篇中,我们将会创建这个表,作为编译过程的一部分,然后将这个表编译进我们的应用中去。
First, let’s remove the check for the
log
andexp
functions inMathFunctions/CMakeLists.txt
. Then remove the check forHAVE_LOG
andHAVE_EXP
frommysqrt.cxx
. At the same time, we can remove#include <cmath>
.
首先,让我们移除MathFunctions/CMakeLists.txt
中的log
和exp
函数。接着移除mysqrt.cxx
文件中的HAVE_LOG
及HAVE_EXP
的检查。与此同时,我们也可以移除#include <cmath>
。
小白按:带大家一起看看删除这两部分之后的这两个文件代码如何:
MathFunctions/CMakeLists.txt
add_library(MathFunctions mysqrt.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# install rules
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
mysqrt.cxx
#include <iostream>
#include "MathFunctions.h"
// a hack square root calculation using simple operations
double mysqrt(double x)
{
if (x <= 0) {
return 0;
}
double result = x;
// do ten iterations
for (int i = 0; i < 10; ++i) {
if (result <= 0) {
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
return result;
}
In the
MathFunctions
subdirectory, a new source file namedMakeTable.cxx
has been provided to generate the table.
在MathFunctions
子目录下,已经新建一个名为MakeTable.cxx
的源文件来生成表。
After reviewing the file, we can see that the table is produced as valid C++ code and that the output filename is passed in as an argument.
重新审视这个文件,我们可以看到这张表是以合法C++代码的形式生成的,并且输出文件作为一个变量传递。
小白按:看一下这个MakeTable.cxx
文件的内容
// A simple program that builds a sqrt table
#include <cmath>
#include <fstream>
#include <iostream>
int main(int argc, char* argv[])
{
// make sure we have enough arguments
if (argc < 2) {
return 1;
}
std::ofstream fout(argv[1], std::ios_base::out);
const bool fileOpen = fout.is_open();
if (fileOpen) {
fout << "double sqrtTable[] = {" << std::endl;
for (int i = 0; i < 10; ++i) {
fout << sqrt(static_cast<double>(i)) << "," << std::endl;
}
// close the table with a zero
fout << "0};" << std::endl;
fout.close();
}
return fileOpen ? 0 : 1; // return 0 if wrote the file
}
通读代码,我们可以看出它是对[0,10)
区间内的整数求取了平方根,当然实际上还是用了系统的sqrt函数。然后将这些值写进了文件名为argv[1]
的文件。在MathFunctions/CMakeLists.txt
中,将会使用调用语句来执行这个代码对应的可执行文件,并向其传递argv[1]
,对于本例来说,当然就是MathFunctions
二进制目录下的Table.h
。
The next step is to add the appropriate commands to the
MathFunctions/CMakeLists.txt
file to build the MakeTable executable and then run it as part of the build process. A few commands are needed to accomplish this.
下一步就是向MathFunctions/CMakeLists.txt
文件中添加合适的命令来建立MakeTable可执行文件,然后执行它,将这个执行的过程当作编译过程的一部分。需要用几行命令来实现。
First, at the top of
MathFunctions/CMakeLists.txt
, the executable forMakeTable
is added as any other executable would be added.
首先,在MathFunctions/CMakeLists.txt
的顶部,向添加其他任意的文件一样,添加MakeTable
的可执行文件。
MathFunctions/CMakeLists.txt
add_executable(MakeTable MakeTable.cxx)
Then we add a custom command that specifies how to produce
Table.h
by running MakeTable.
然后我们添加一条自定义命令,指定如何通过运行MakeTable的方式生成Table.h
MathFunctions/CMakeLists.txt
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
Next we have to let CMake know that
mysqrt.cxx
depends on the generated fileTable.h
. This is done by adding the generatedTable.h
to the list of sources for the library MathFunctions.
接下来我们必须让CMake知道mysqrt.cxx
是依赖于生成的文件Table.h
之上的。这通过向MathFunctions库中的源码列表添加已生成的Table.h
来实现。
MathFunctions/CMakeLists.txt
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
We also have to add the current binary directory to the list of include directories so that
Table.h
can be found and included bymysqrt.cxx
.
我们同时也不得不把当前的二进制目录添加进包含路径中,这样Table.h
才能够被mysqrt.cxx
所发现并包含。
MathFunctions/CMakeLists.txt
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
小白按:修改完成后,展示一下最终的MathFunctions/CMakeLists.txt
:
add_executable(MakeTable MakeTable.cxx)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
# install rules
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
Now let’s use the generated table. First, modify
mysqrt.cxx
to includeTable.h
. Next, we can rewrite themysqrt
function to use the table:
现在让我们来使用生成的表。首先,修改mysqrt.cxx
来包含Table.h
。接下来,我们可以重写mysqrt
函数来使用表:
MathFunctions/mysqrt.cxx
double mysqrt(double x)
{
if (x <= 0) {
return 0;
}
// use the table to help find an initial value
double result = x;
if (x >= 1 && x < 10) {
std::cout << "Use the table to help find an initial value " << std::endl;
result = sqrtTable[static_cast<int>(x)];
}
// do ten iterations
for (int i = 0; i < 10; ++i) {
if (result <= 0) {
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
return result;
}
小白按: 尽管没什么必要,小白还是展示一下完整的该文件
#include <iostream>
#include "Table.h"
#include "MathFunctions.h"
double mysqrt(double x)
{
if (x <= 0) {
return 0;
}
// use the table to help find an initial value
double result = x;
if (x >= 1 && x < 10) {
std::cout << "Use the table to help find an initial value " << std::endl;
result = sqrtTable[static_cast<int>(x)];
}
// do ten iterations
for (int i = 0; i < 10; ++i) {
if (result <= 0) {
result = 0.1;
}
double delta = x - (result * result);
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
return result;
}
Run the
cmake
executable or thecmake-gui
to configure the project and then build it with your chosen build tool.
运行 cmake
可执行文件或 cmake-gui
来指定项目,然后用你选定的工具来进行编译。
When this project is built it will first build the
MakeTable
executable. It will then runMakeTable
to produceTable.h
. Finally, it will compilemysqrt.cxx
which includesTable.h
to produce theMathFunctions
library.
当这个项目编译时,它首先生成MakeTable
可执行文件。然后它会运行这个MakeTable
来创建Table.h
。最后,它将会编译包含了Table.h
的mysqrt.cxx
来生成MathFunctions
库。
Run the Tutorial executable and verify that it is using the table.
运行Tutorial 可执行文件,验证它使用了这个表。
小白按:编译过程省略,没有什么坑点。编译完以后你可以在/Step6_build/MathFunctions
找到Table.h
文件,向大家展示一下这个文件的内容:
Table.h
double sqrtTable[] = {
0,
1,
1.41421,
1.73205,
2,
2.23607,
2.44949,
2.64575,
2.82843,
3,
0};
进入编译好的debug文件夹执行一下看看:
Tutorial 7.2
Use the table to help find an initial value
Computing sqrt of 7.2 to be 2.68355
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
The square root of 7.2 is 2.68328
其实这个算法还是沿用了之前编写的那个逻辑,只是对于[0,10)
范围内的值给定了一个迭代的初始值。作为对比,我们来看一看已经学习过的第2步中的函数运行的结果:
Tutorial 7.2
Computing sqrt of 7.2 to be 4.1
Computing sqrt of 7.2 to be 2.92805
Computing sqrt of 7.2 to be 2.69351
Computing sqrt of 7.2 to be 2.6833
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
Computing sqrt of 7.2 to be 2.68328
The square root of 7.2 is 2.68328
下一篇我们将学习如何打包一个安装文件。
【水平所限,错漏难免,创作不易,轻喷勿骂】