Elixir Cross Referencer 是一个非常好用的 C/C++ 代码索引工具。它提供了一个 Web UI,可以选择不同的项目代码进行阅读,并支持搜索和进行符号跳转。本文讲述如何搭建自己的 Elixir Cross Referencer,并以 FreeBSD 为例,说明如何适配自己的代码仓库。
本教程的成品展示:https://elixir.catboy.me
目录
- 为什么需要适配自己的代码仓库?
- 安装 Elixir Cross Referencer
- 下载 FreeBSD 源码
- 为 FreeBSD 适配 Elixir
- 索引 FreeBSD 源码
- 配置 Apache2 网页服务器
- Elixir 的日常维护
为什么需要适配自己的代码仓库?
在 Elixir Cross Referencer 的 Web UI 左侧边栏,包含一个版本列表,可以让用户快速切换同一代码库的不同版本。这一功能,是通过列出 Git 代码仓库的所有 Tag 实现的。Elixir 会要求用户实现几个 Shell 函数,来列出 Git 仓库的所有标签,以及将所有标签的字符串,按照主次版本号分割。如下图中的版本号 “v6 / v6.5 / v6.5.5
”。
默认情况下,Elixir 使用 “vX.X.XXX
” 字符串匹配模式来显示版本层次,如 Linux,U-Boot,Zephyr 等。如果某个仓库的 Git 标签不遵循这种模式,那么你需要支持这个仓库。否则,版本列表中会显示 “FIXME” 字样,表示版本替换功能不可用。
安装 Elixir Cross Referencer
本教程基于 Ubuntu 22.04 LXC 容器,并采用直接安装,而非使用 Docker 进行部署。
Elixir 的安装过程,和官方 GitHub 所描述的过程一致:
# 依赖项目
apt install python3-jinja2 python3-bsddb3 python3-falcon python3-pytest python3-pygments universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4
# Elixir 会被安装在 /usr/local/elixir/
git clone https://github.com/bootlin/elixir.git /usr/local/elixir/
下载 FreeBSD 源码
本文使用 /opt/elixir-data
作为 Elixir 项目目录。
# 创建 FreeBSD 的 Elixir 索引缓存目录
mkdir -p /opt/elixir-data/freebsd/data
# 拉取 FreeBSD 源码
git clone https://github.com/freebsd/freebsd-src.git /opt/elixir-data/freebsd/repo
# (!!关键!!) 更改所有者为 www-data,否则会报错 "This file does not exist"
chown -R www-data /opt/elixir-data/freebsd/repo
为 FreeBSD 适配 Elixir
适配一个代码仓库,需要编写一个 Shell 脚本,并重新实现一些 Shell 函数。这个 Shell 脚本需要放在 “/usr/local/elixir/projects
” 目录中,并应命名为 “<项目名称>.sh
”。当访问 Elixir Web UI 时,会调用此 Shell 脚本中实现的函数,来获取最新版本,以及版本层次。
要适配 FreeBSD,我们需要在 “/usr/local/elixir/projects/
” 目录内创建名为 “freebsd.sh
” 的文件。文件的内容如下:
# Elixir definitions for FreeBSD
# Enable DT bindings compatible strings support
dts_comp_support=1
list_tags()
{
echo "$tags"
}
list_tags_h()
{
echo "$tags" | tac | sed -E 's/(release\/[0-9]+)(\.[0-9]+)?(\.[0-9a-zA-Z_]+)?/\1 /'
}
get_latest()
{
git tag | sort -V | tail -n 1
}
详细说明一下 freebsd.sh
文件中各个部分的作用:
- 变量
dts_comp_support=1
这个变量启用了 Elixir 对设备树(Device Tree)的支持。设备树是用来描述硬件设备信息的数据结构,在嵌入式系统中非常常见。 - 函数
list_tags()
该函数负责过滤 Git 标签。函数被调用时,有一个预定义好的变量$tags
,其值是一个字符串,包含了所有 Git 标签。你需要过滤这些标签,去除那些你不想在网页界面上显示的标签,并将剩下的标签打印到标准输出(stdout)。 - 函数
list_tags_h()
该函数用于将 Git 标签拆分成三部分的字符串,以显示版本层次结构。例如,如果 FreeBSD 有一个版本标签release/13.2.0
,你需要将其拆分为以下字符串,并将其打印至标准输出:“release/13 release/13.2 release/13.2.0”
- 函数
get_latest()
:
该函数用于获取仓库的最新版本。当请求 URLhttps://<elixir>/<repository>/latest/source
时,会调用get_latest()
函数来获取最新版本。如果该函数没有实现,你会收到一个 404 错误。
索引 FreeBSD 源码
要在 Elixir 的 Web UI 上进行符号搜索和跳转,需要首先对代码进行索引。Elixir 会对代码进行分析,并将代码符号索引存入数据库。
在索引之前,首先要使用 Elixir 自带的测试脚本,来验证先前的适配是否正常。首先来测试能否列出 FreeBSD 所有的 Git 标签:
root@elixir:~# cd /usr/local/elixir
root@elixir:/usr/local/elixir# LXR_REPO_DIR=/opt/elixir-data/freebsd/repo/ LXR_DATA_DIR=/opt/elixir-data/freebsd/data/ ./script.sh list-tags
release/1.0.0_cvs
release/1.1.0_cvs
release/1.1.5.1_cvs
release/2.0
release/2.0.5
release/2.0.5_cvs
release/2.0_cvs
release/2.1.0
...
看起来并没有毛病。再试试版本的字符串分割是否有效:
root@elixir:/usr/local/elixir# LXR_REPO_DIR=/opt/elixir-data/freebsd/repo/ LXR_DATA_DIR=/opt/elixir-data/freebsd/data/ ./script.sh list-tags -h
release/13 release/13.2 release/13.2.0
release/13 release/13.1 release/13.1.0
release/13 release/13.0 release/13.0.0
release/12 release/12.4 release/12.4.0
release/12 release/12.3 release/12.3.0
release/12 release/12.2 release/12.2.0
release/12 release/12.1 release/12.1.0
release/12 release/12.0 release/12.0.0
...
也没有问题。接下来就可以开始建立索引了。我的机器性能很强,我开了 16 个 Job 来建立索引:
root@elixir:/usr/local/elixir# LXR_REPO_DIR=/opt/elixir-data/freebsd/repo/ LXR_DATA_DIR=/opt/elixir-data/freebsd/data/ ./update.py 16
对于 FreeBSD 这种超大型项目来说,建立索引需要很长的时间。在我的机器上,建立索引用了 20 个小时。当索引建立完成后,可以使用 Elixir 的测试脚本来尝试搜索一个符号,来验证先前的工作是否都成功完成:
root@elixir:/usr/local/elixir# LXR_REPO_DIR=/opt/elixir-data/freebsd/repo/ LXR_DATA_DIR=/opt/elixir-data/freebsd/data/ ./query.py release/13.2.0 ident fuse_filehandle_open C
Symbol Definitions:
Symbol in path: sys/fs/fuse/fuse_file.h, line: 216 , type: prototype
Symbol in path: sys/fs/fuse/fuse_file.c, line: 124 , type: function
Symbol References:
Symbol in path: sys/fs/fuse/fuse_vnops.c, line: 1763,1860,1942,2478
Documented in:
...
看起来都没有问题。
配置 Apache2 网页服务器
Apache2 的安装以及配置方法,不在本文的讨论范围内。这里我直接提供我的 Apache2 的配置文件:
HttpProtocolOptions Unsafe
# Required for HTTP
<Directory /usr/local/elixir/http/>
Options +ExecCGI
AllowOverride None
Require all granted
SetEnv PYTHONIOENCODING utf-8
SetEnv LXR_PROJ_DIR /opt/elixir-data
</Directory>
# Required for the REST API
<Directory /usr/local/elixir/api/>
SetHandler wsgi-script
Require all granted
SetEnv PYTHONIOENCODING utf-8
SetEnv LXR_PROJ_DIR /opt/elixir-data
</Directory>
AddHandler cgi-script .py
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin mail@example.com
DocumentRoot /usr/local/elixir/http
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
AllowEncodedSlashes On
RewriteEngine on
RewriteRule "^/$" "/freebsd/latest/source" [R]
RewriteRule "^/(?!api|acp).*/(source|ident|search)" "/web.py" [PT]
RewriteRule "^/acp" "/autocomplete.py" [PT]
ServerName elixir.example.com
SSLCertificateFile /etc/letsencrypt/live/elixir.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/elixir.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
记得更换文件中的域名哦。
Elixir 的日常维护
Elixir 没有很大的维护成本。通常来讲只有代码仓库更新,需要重新建立索引时,才需要进行操作。
如果你需要删掉 Elixir Web UI 顶部的 Bootlin LOGO,清空这个文件即可:
root@elixir:/usr/local/elixir# :> /usr/local/elixir/templates/header.html
完成的成品如下图所示:
我的 GitHub 上有两个脚本可以一键完成添加新代码仓库,以及重新索引代码仓库。可以进行参考。