返回介绍

6.2 使用 Python 在配置时生成源码

发布于 2025-05-06 21:45:57 字数 4889 浏览 0 评论 0 收藏 0

NOTE : 此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-6/recipe-02 中找到,其中包含一个 Fortran/C 例子。该示例在 CMake 3.10 版(或更高版本) 中是有效的,并且已经在 GNU/Linux、macOS 和 Windows(使用 MSYS Makefile) 上进行过测试。

本示例中,我们将再次从模板 print_info.c.in 生成 print_info.c 。但这一次,将假设 CMake 函数 configure_file() 没有创建源文件,然后使用 Python 脚本模拟这个过程。当然,对于实际的项目,我们可能更倾向于使用 configure_file() ,但有时使用 Python 生成源代码的需要时,我们也应该知道如何应对。

这个示例有严重的限制,不能完全模拟 configure_file() 。我们在这里介绍的方法,不能生成一个自动依赖项,该依赖项将在构建时重新生成 print_info.c 。换句话说,如果在配置之后删除生成的 print_info.c ,则不会重新生成该文件,构建也会失败。要正确地模拟 configure_file() ,需要使用 add_custom_command()add_custom_target() 。我们将在第 3 节中使用它们,来克服这个限制。

这个示例中,我们将使用一个简单的 Python 脚本。这个脚本将读取 print_info.c.in 。用从 CMake 传递给 Python 脚本的参数替换文件中的占位符。对于更复杂的模板,我们建议使用外部工具,比如 Jinja(参见 http://jinja.pocoo.org )。

def configure_file(input_file, output_file, vars_dict):
​
  with input_file.open('r') as f:
      template = f.read()
​
  for var in vars_dict: 
      template = template.replace('@' + var + '@', vars_dict[var])
​
  with output_file.open('w') as f:
      f.write(template)

这个函数读取一个输入文件,遍历 vars_dict 变量中的目录,并用对应的值替换 @ [email protected] ,再将结果写入输出文件。这里的键值对,将由 CMake 提供。

准备工作

print_info.c.inexample.f90 与之前的示例相同。此外,我们将使用 Python 脚本 configurator.py ,它提供了一个函数:

def configure_file(input_file, output_file, vars_dict):
  with input_file.open('r') as f:
      template = f.read()
​
  for var in vars_dict:
      template = template.replace('@' + var + '@', vars_dict[var])
​
  with output_file.open('w') as f:
      f.write(template)

该函数读取输入文件,遍历 vars_dict 字典的所有键,用对应的值替换模式 @ [email protected] ,并将结果写入输出文件(键值由 CMake 提供)。

具体实施

与前面的示例类似,我们需要配置一个模板文件,但这一次,使用 Python 脚本模拟 configure_file() 函数。我们保持 CMakeLists.txt 基本不变,并提供一组命令进行替换操作 configure_file(print_info.c.in print_info.c @ONLY) ,接下来将逐步介绍这些命令:

  1. 首先,构造一个变量 _config_script ,它将包含一个 Python 脚本,稍后我们将执行这个脚本:
    set(_config_script
    "
    from pathlib import Path
    source_dir = Path('${CMAKE_CURRENT_SOURCE_DIR}')
    binary_dir = Path('${CMAKE_CURRENT_BINARY_DIR}')
    input_file = source_dir / 'print_info.c.in'
    output_file = binary_dir / 'print_info.c'
    ​
    import sys
    sys.path.insert(0, str(source_dir))
    ​
    from configurator import configure_file
    vars_dict = {
      '_user_name': '${_user_name}',
      '_host_name': '${_host_name}',
      '_fqdn': '${_fqdn}',
      '_processor_name': '${_processor_name}',
      '_processor_description': '${_processor_description}',
      '_os_name': '${_os_name}',
      '_os_release': '${_os_release}',
      '_os_version': '${_os_version}',
      '_os_platform': '${_os_platform}',
      '_configuration_time': '${_configuration_time}',
      'CMAKE_VERSION': '${CMAKE_VERSION}',
      'CMAKE_GENERATOR': '${CMAKE_GENERATOR}',
      'CMAKE_Fortran_COMPILER': '${CMAKE_Fortran_COMPILER}',
      'CMAKE_C_COMPILER': '${CMAKE_C_COMPILER}',
    }
    configure_file(input_file, output_file, vars_dict)
    ")
  2. 使用 find_package 让 CMake 使用 Python 解释器:
    find_package(PythonInterp QUIET REQUIRED)
  3. 如果找到 Python 解释器,则可以在 CMake 中执行 _config_script ,并生成 print_info.c 文件:
    execute_process(
      COMMAND
          ${PYTHON_EXECUTABLE} "-c" ${_config_script}
      )
  4. 之后,定义可执行目标和依赖项,这与前一个示例相同。所以,得到的输出没有变化。

工作原理

回顾一下对 CMakeLists.txt 的更改。

我们执行了一个 Python 脚本生成 print_info.c 。运行 Python 脚本前,首先检测 Python 解释器,并构造 Python 脚本。Python 脚本导入 configure_file 函数,我们在 configurator.py 中定义了这个函数。为它提供用于读写的文件位置,并将其值作为键值对。

此示例展示了生成配置的另一种方法,将生成任务委托给外部脚本,可以将配置报告编译成可执行文件,甚至库目标。我们在前面的配置中认为的第一种方法更简洁,但是使用本示例中提供的方法,我们可以灵活地使用 Python(或其他语言),实现任何在配置时间所需的步骤。使用当前方法,我们可以通过脚本的方式执行类似 cmake_host_system_information() 的操作。

但要记住,这种方法也有其局限性,它不能在构建时重新生成 print_info.c 的自动依赖项。下一个示例中,我们应对这个挑战。

更多信息

我们可以使用 get_cmake_property(_vars VARIABLES) 来获得所有变量的列表,而不是显式地构造 vars_dict (这感觉有点重复),并且可以遍历 _vars 的所有元素来访问它们的值:

get_cmake_property(_vars VARIABLES)
foreach(_var IN ITEMS ${_vars})
  message("variable ${_var} has the value ${${_var}}") 
endforeach()

使用这种方法,可以隐式地构建 vars_dict 。但是,必须注意转义包含字符的值,例如: ; , Python 会将其解析为一条指令的末尾。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。