记录 llvm 获取 tag 的脚本与 lldb 不兼容问题解决过程

不兼容问题描述

负责 llvm 研发的同事在 cmake 脚本里补充了一个小功能,来获取 llvm 仓库的版本号 tag,实现原理是在官方的生成版本脚本里添加了一个 tag 变量,该 tag 变量值通过执行命令 git describe --contains HEAD 来获取。但是实现过程中,在代码里包含了 ${LLVM_SOURCE_DIR} 路径变量,编译 llvm 时不会报错,但当编译 lldb 时这个 cmake 脚本就会报错,提示找不到这个路径变量,从而产生了不兼容问题。

尝试解决方案

尝试了两个解决方法,实验后发现都不可行。

  • 尝试了在构建 lldb 的时候,手动指定这个变量值 - DLLVM_SOURCE_DIR=path/to/llvm/source,但是并不能传递过去,分析发现,该变量值需要通过调用该脚本的那个调用者传递,而不能通过最外层构建 lldb 时传递。

  • ${LLVM_SOURCE_DIR} 改成 llvm 项目里更常见的变量 ${LLVM_ROOT_SRC},还是不识别。

深入分析问题

发现添加该功能时,修改的脚本是:llvm-project/llvm/cmake/modules/GenerateVersionFromVCS.cmake,该脚本是属于工具类脚本,可以指定输入参数后进行调用。

1
2
3
4
5
6
# CMake script that writes version control information to a header.
#
# Input variables:
# NAMES - A list of names for each of the source directories.
# <NAME>_SOURCE_DIR - A path to source directory for each name in NAMES.
# HEADER_FILE - The header file to write

经过分析发现,这个脚本里的变量都是通过输入参数解析传递过来的,llvm 编译时之所以能识别到 ${LLVM_SOURCE_DIR} 路径变量,是因为这里通过 -DLLVM_SOURCE_DIR 指定了传参:

1
2
3
4
5
6
7
8
9
10
11
12
set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake")

COMMAND ${CMAKE_COMMAND} "-DNAMES=LLVM"
"-DLLVM_SOURCE_DIR=${llvm_source_dir}"
"-DHEADER_FILE=${version_inc}"
-P "${generate_vcs_version_script}")

COMMAND ${CMAKE_COMMAND} "-DNAMES=\"LLVM;CLANG\""
"-DLLVM_SOURCE_DIR=${llvm_source_dir}"
"-DCLANG_SOURCE_DIR=${clang_source_dir}"
"-DHEADER_FILE=${version_inc}"
-P "${generate_vcs_version_script}")

而在 lldb 源码只指定 -DLLDB_SOURCE_DIR 传参,故而识别不到 -DLLVM_SOURCE_DIR,lldb 里是这样调用脚本的:

1
2
3
4
5
6
add_custom_command(OUTPUT "${version_inc}"
DEPENDS "${lldb_vc}" "${generate_vcs_version_script}"
COMMAND ${CMAKE_COMMAND} "-DNAMES=LLDB"
"-DLLDB_SOURCE_DIR=${LLDB_SOURCE_DIR}"
"-DHEADER_FILE=${version_inc}"
-P "${generate_vcs_version_script}")

最终解决方案

分析了 cmake 脚本传参的特点,发现 NAMES 参数取值可能是 LLDB 单个形式或者 LLVM;CLANG 列表形式,于是通过 list(GET NAMES 0 current_name) 获取参数 NAMES 的第一个参数。然后再把 ${LLVM_ROOT_SRC} 换成 ${${current_name}_SOURCE_SRC} 就可以了。

学习 cmake 语法

在这个过程中,自己查阅资料,也学习了一些 cmake 语法知识。

  1. 通过 execute_process 可以在 cmake 里面执行 shell 命令或脚本,OUTPUT_VARIABLE 参数对应输出的内容。例如:

    execute_process (COMMAND echo “Hello World” OUTPUT_VARIABLE output)// 执行命令 echo “Hello World”,输出到 output

  2. cmake 中字符串可以通过 string 进行操作。例如:

string (SUBSTRING “Hello World” 0 5 S_sub)// 获取索引 0,长度 5 的子串 Hello

  1. 通过 list 命令来处理 cmake 中的字符串列表。例如:

list (GET input_list 0 element_0)// 获取列表 input_list 中索引 0 的元素,输出到 element_0