6.2 使用Python在配置时生成源码
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 )。
这个函数读取一个输入文件,遍历vars_dict
变量中的目录,并用对应的值替换@key@
,再将结果写入输出文件。这里的键值对,将由CMake提供。
准备工作
print_info.c.in
和example.f90
与之前的示例相同。此外,我们将使用Python脚本configurator.py
,它提供了一个函数:
该函数读取输入文件,遍历vars_dict
字典的所有键,用对应的值替换模式@key@
,并将结果写入输出文件(键值由CMake提供)。
具体实施
与前面的示例类似,我们需要配置一个模板文件,但这一次,使用Python脚本模拟configure_file()
函数。我们保持CMakeLists.txt基本不变,并提供一组命令进行替换操作configure_file(print_info.c.in print_info.c @ONLY)
,接下来将逐步介绍这些命令:
首先,构造一个变量
_config_script
,它将包含一个Python脚本,稍后我们将执行这个脚本:使用
find_package
让CMake使用Python解释器:如果找到Python解释器,则可以在CMake中执行
_config_script
,并生成print_info.c
文件:之后,定义可执行目标和依赖项,这与前一个示例相同。所以,得到的输出没有变化。
工作原理
回顾一下对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
的所有元素来访问它们的值:
使用这种方法,可以隐式地构建vars_dict
。但是,必须注意转义包含字符的值,例如:;
, Python会将其解析为一条指令的末尾。
Last updated