CMake官方文档
参考官方cmake3.24教程翻译。我这里使用cmake 3.16来演示例子。
https://cmake.org/cmake/help/v3.24/guide/tutorial/index.html
https://gitlab.kitware.com/cmake/cmake/-/tree/master/Help/guide/tutorial
step6
https://cmake.org/cmake/help/v3.24/guide/tutorial/Adding%20a%20Custom%20Command%20and%20Generated%20File.html
我的仓库 :
https://github.com/FRBoiling/cmake-tutorial.git
假设,出于本教程的目的,我们决定永远不要使用平台log和exp函数,而是要生成一个预先计算值的表,用于mysqrt函数。
在本节中,我们将创建表作为构建过程的一部分,然后将该表编译到我们的应用程序中。
初始化
首先,让我们删除MathFunctions/CMakeLists.txt中log和exp函数的检查。
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中删除对HAVE_LOG和HAVE_EXP的检查。同时,我们可以删除#include 。
double mysqrt(double x)
{
if (x <= 0)
{
return 0;
}
// #if defined(HAVE_LOG) && defined(HAVE_EXP)
// double result = std::exp(std::log(x) * 0.5);
// std::cout << "Computing sqrt of " << x << " to be " << result
// << " using log and exp" << std::endl;
// #else
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;
}
// #endif
return result;
}
在MathFunctions目录中,新增MakeTable.cxx的源文件,用来生成table。我们可以看到table是作为有效的c++代码生成的,并且输出文件名作为参数传入。
// 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
}
自定义的命令生成
下一步是向MathFunctions/CMakeLists.txt文件添加适当的命令,以构建MakeTable可执行文件,然后作为构建过程的一部分运行它。需要几个命令来完成此操作。
首先,在MathFunctions/CMakeLists.txt的顶部,添加MakeTable的可执行文件
add_executable(MakeTable MakeTalbe.cxx)
然后添加一个自定义命令,指定如何通过运行MakeTable生成table.h
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
接下来我们要让CMake知道mysqrt。cxx依赖于生成的文件table.h。这是通过将生成的table.h添加到标准库MathFunctions的源列表来实现的。
add_library(MathFunctions
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
我们还必须将当前的二进制目录添加到include目录列表中,以便mysqrt.cxx能够找到并包含table.h。
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
使用生成的表
首先,修改mysqrt.cxx包含table.h。接下来,我们可以重写mysqrt函数来使用这个表:
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;
}
测试练习
运行cmake可执行文件或cmake-gui来配置项目,然后使用您选择的构建工具构建它。
当这个项目被构建时,它将首先构建MakeTable可执行文件。然后运行MakeTable生成table.h。最后,它将编译mysqrt.cxx,其中包括table.h来生成MathFunctions库。
运行Tutorial可执行文件并验证它是否使用了表。