cmake 关键源码分析

0,前置知识点

0.1 cmake 是一个脚本解释器

cmake 本身是一个脚语言本解释器,用于解释执行 cmake 脚本语言程序;

示例:

0.2 cmake 可以用于构建

利用 cmake 脚本语言的功能,再加上一些内置的构建相关的命令,构成了 cmake 构建系统。

1,编译 debug 版本 cmake

下载cmake 源代码

​$ wget https://github.com/Kitware/CMake/releases/download/v3.31.7/cmake-3.31.7.tar.gz

编译 debug 版本 cmake:

./bootstrap --prefix=/usr/local/cmake-debug \
            --parallel=$(nproc) \
            --no-system-libs \
            -- \
            -DCMAKE_BUILD_TYPE=Debug \
            -DCMAKE_C_FLAGS="-g -O0" \
            -DCMAKE_CXX_FLAGS="-g -O0"
$ make -j

不需要执行安装命令:make install

验证:

2,cmake 执行脚本程序

cmake 作为一个 可执行程序,需要存在其入口 main 函数:

./cmake-3.31.7/Source/cmakemain.cxx line 1128

int main(int ac, char const* const* av)
{
  cmSystemTools::EnsureStdPipes();

  // Replace streambuf so we can output Unicode to console
  auto consoleBuf = cm::make_unique<cmConsoleBuf>();
  consoleBuf->SetUTF8Pipes();


...


  int ret = do_cmake(ac, av);
#ifndef CMAKE_BOOTSTRAP
  cmDynamicLoader::FlushCache();
#endif
  if (uv_loop_t* loop = uv_default_loop()) {
    uv_loop_close(loop);
  }
  return ret;
}

上述代码中,cmake 的主要配置和生成项目构建文件的功能是由函数 do_cmake(ac, av); 解释执行 完成其功能。

cmake 脚本语言中的内置脚本命令和内置项目构建命令,是由原文件:./cmake-3.31.7/Source/cmCommands.cxx

通过 如下三个函数加入到解释器中的:

void GetScriptingCommands(cmState* state);
void GetProjectCommands(cmState* state);
void GetProjectCommandsInScriptMode(cmState* state);

以 第0节中使用的 message 命令为例,在 GetScripting Commands()中添加:


void GetScriptingCommands(cmState* state)
{
  state->AddFlowControlCommand("break", cmBreakCommand);
  state->AddFlowControlCommand("continue", cmContinueCommand);
  state->AddFlowControlCommand("foreach", cmForEachCommand);
  state->AddFlowControlCommand("function", cmFunctionCommand);
  state->AddFlowControlCommand("if", cmIfCommand);
  state->AddFlowControlCommand("macro", cmMacroCommand);
  state->AddFlowControlCommand("return", cmReturnCommand);
  state->AddFlowControlCommand("while", cmWhileCommand);
  state->AddFlowControlCommand("block", cmBlockCommand);

  state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand);
  state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired);
...

  state->AddBuiltinCommand("include", cmIncludeCommand);
...
  state->AddBuiltinCommand("message", cmMessageCommand);
...

}

实际调用的函数为 cmMessageCommand();

同时可以发现这里定义了 if 等控制命令。

常用的 link_directories 和 project 等等命令是在如下函数中加入:

void GetProjectCommands(cmState* state)
{
  state->AddBuiltinCommand("add_executable", cmAddExecutableCommand);
  state->AddBuiltinCommand("add_library", cmAddLibraryCommand);
  state->AddBuiltinCommand("add_subdirectory", cmAddSubDirectoryCommand);
...
  state->AddBuiltinCommand("enable_language", cmEnableLanguageCommand);
...
  state->AddBuiltinCommand("include_directories", cmIncludeDirectoryCommand);
...
  state->AddBuiltinCommand("link_directories", cmLinkDirectoriesCommand);
  state->AddBuiltinCommand("project", cmProjectCommand);
...
}

3,调试 cmake 内置命令

以 message 为例,即调试 cmMessageCommand() 函数。

利用第1节中的 gdb --args ../../bin/cmake -P ./hello1.cmake 来调试 cmMessageCommand;

在进入 gdb后执行断点命令:

(gdb )break    cmMessageCommand

(gdb ) start

(gdb ) continue

便会停在 cmMessageCommand() 函数的入口:

进一步跟踪后会发现,本例子中,通过如下这句

    case Message::LogLevel::LOG_NOTICE:
      cmSystemTools::Message(IndentText(message, mf));

最终调用了std:: 的流:

std::cerr << m << std::endl;

详细代码如下:

void cmSystemTools::Message(const std::string& m, const char* title)
{
  cmMessageMetadata md;
  md.title = title;
  Message(m, md);
}

void cmSystemTools::Message(const std::string& m, const cmMessageMetadata& md)
{
  if (s_MessageCallback) {
    s_MessageCallback(m, md);
  } else {
    std::cerr << m << std::endl;
  }
}

        当 gdb 停在 cmMessageCommand 函数时,可以执行 (gdb )bt,查看调用堆栈:

其中,函数:
         cmMakefile::RunListFile ( )
         cmMakefile::ReadListFile ( )

是执行具体脚本文件的起点,其参数中出现了脚本名字 hello01.cmake,实现读入和解析执行具体动作。

其他内置命令也可以这样debug。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值