Ubuntu 22.04 编译安装 Apache Lucy 0.6.2 指南

Ubuntu 22.04 从源码编译安装 Apache Lucy 0.6.2 的完整实测记录:先构建 Clownfish 编译器与运行库,再编译 Lucy C 库,覆盖依赖准备、官方地址校验下载、configure/make、安装布局验证、测试套件运行与排障实录。

Apache LucyLucyApache安装部署Ubuntu 22.04C/C++2026/10/15

Ubuntu 22.04 编译安装 Apache Lucy 0.6.2 指南

本文全部命令与回显均在 Ubuntu 22.04(内核 5.15)服务器上实测执行,工具链为 gcc 11.4.0 与 GNU Make 4.3。Lucy 已于 2019 年 11 月进入 Apache Attic,本文安装的是其终版 0.6.2,适用于学习检索引擎原理与维护存量遗产系统,不建议新项目采用。

一、安装目标与整体路线

1.1 安装目标

从零开始,在一台全新安装的 Ubuntu 22.04 服务器上,把 Apache Lucy 0.6.2 的 C 语言库完整编译、安装并验证可用,最终得到以下产物:

  • 可执行文件 /usr/local/bin/cfc(Clownfish 编译器,负责把 .cfh 类描述文件生成为 C 头文件与引导代码,实测约 1.6 MB);
  • 共享库 /usr/local/lib/libclownfish.so.0.6.2(Clownfish 运行库,实测 543,696 字节);
  • 共享库 /usr/local/lib/liblucy.so.0.6.2(Lucy 检索引擎核心,实测 6,450,800 字节,含调试信息未 strip);
  • 全部类描述文件 /usr/local/share/clownfish/include/(含 Clownfish、Lucy、LucyX 三个 Parcel,共 153 份 .cfh 类描述);
  • lucy.pc 与 clownfish.pc pkg-config 描述文件(供 pkg-config 工具自动拼装编译参数);
  • 官方 C 测试套件全部通过的确认(65 批次、5770 项断言)。

1.2 三步走的构建顺序

Lucy 不能直接编译,这是它安装流程区别于多数 C 项目的关键点。Lucy 的每个类都声明在 .cfh 文件中,构建前必须先用 Clownfish 编译器 cfc 把这些描述文件生成为真正的 C 头文件和方法表。因此完整安装路线固定为三步:

  1. 编译安装 Clownfish 编译器(cfc)——一个独立的可执行程序;
  2. 编译安装 Clownfish 运行库(libclownfish)——Lucy 的基础对象模型;
  3. 编译安装 Lucy C 库(liblucy)——检索引擎本体,构建过程会调用 cfc 生成代码。

需要注意的是,Lucy 官方源码包中的 INSTALL 文件只描述了 Perl 绑定路线(实测摘录原文):

Build Instructions
==================
The Apache Lucy search engine library provides full-text search for dynamic
programming languages.  Currently it is available via Perl bindings.
To build Lucy with Perl bindings, chdir to the "perl" subdirectory underneath
the "lucy" top level and follow the instructions in the INSTALL document
there.

而 Lucy 源码树中自带的 C 语言路线(顶层 c/ 目录,内含独立的 configure 与 install.sh)在 INSTALL 中并未提及,本文实测的正是这条 C 路线。

两条路线的差异用一张对照表说清,便于按目标选路:

维度C 路线(本文)Perl 绑定路线(INSTALL 描述)
最终产物liblucy.so / libclownfish.so / cfcPerl 模块 Lucy.xs 编译出的 .so 与 .pm 文件
依赖gcc、make、flex、git上表之外再加 Perl 5 开发头文件与 ExtUtils::MakeMaker
适用对象C/C++ 程序嵌入、源码研读Perl 程序调用检索能力
测试入口c/ 下 make test(65 批次)perl/ 下 make test(Perl 测试体系)
交付形态独立库 + 头文件 + man 页语言扩展模块

两条路线共享同一套 core/ 实现与 .cfh 描述——这正是 Clownfish 体系的设计目的:一份实现、多宿主绑定。因此无论走哪条路,理解 .cfh 到 C 代码的生成机制都是前置知识;本文选择 C 路线的另一个理由是排障面更短:绕开宿主语言工具链后,问题只可能出在「编译器、库链接、生成代码」三层之内。

1.3 环境与工具版本

本文实测环境组件版本如下(均通过命令实际查询):

$ gcc --version | head -1
gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

$ make --version | head -1
GNU Make 4.3

$ flex --version
flex 2.6.4

磁盘占用方面:源码包 886,072 字节,解压后源码树约 30 MB,构建中间产物约 200 MB,最终安装产物(两个共享库加全部头文件)约 6 MB,整条路线对磁盘与内存的要求都很低。

二、系统准备

2.1 安装编译工具链

Ubuntu 22.04 全新系统需要补齐编译器、make、词法分析器生成器 flex 和 git(用于拉取 Clownfish 源码):

apt-get update
apt-get install -y build-essential flex git

其中 flex 最容易被忽视,但它是硬依赖:Clownfish 编译器的词法分析源码 CFCLexHeader.l 必须由 flex 生成对应的 C 文件,缺失时 configure 阶段不会报任何错误,直到 make 阶段才失败(详见第九章排障实录第一条)。

另一类「生成器」则不需要系统安装:源码树自带的 lemon/ 目录内含 lemon 解析器生成器(SQLite 作者的 LALR 工具)及其模板 lempar.c,构建时现编现用,Lucy 的 JSON 解析器即由 ../core/Lucy/Util/Json/JsonParser.y 经 lemon 生成(Makefile 实测规则 JsonParser.c : JsonParser.y $(LEMON_EXE))。两套生成器的分工值得记住:flex 生成词法层(cfc 解析 .cfh 的记号扫描),lemon 生成语法层(JsonParser 的文法归约),前者必须预装、后者源码自带——这是「哪些依赖必须提前装」的准确边界,也是读 Makefile 时区分两类生成规则的依据。

2.2 确认基础工具就绪

$ gcc --version | head -1
gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

$ make --version | head -1
GNU Make 4.3

系统自带 Perl 5 即可,C 路线全程不需要额外 Perl 模块;本文也完全不涉及 Perl 绑定的安装。

三、获取源码包并校验

3.1 官方下载地址

Lucy 已退役,其发行包不再出现在常规分发站点上。实测使用以下两个地址验证:CDN 分发站 dlcdn.apache.org 上 /dist/lucy/ 路径返回 404;完整发行历史只保留在 Apache 归档站点 archive.apache.org。0.6.2 的源码包为归档站点 /dist/lucy/ 目录下的扁平文件(不在版本子目录中):

https://archive.apache.org/dist/lucy/apache-lucy-0.6.2.tar.gz

归档站点对下载速率有刻意的限制(响应头会携带 Vary: Slow,Glacial),866 KB 的包通常也要下载数十秒,属于正常现象。供应链校验同样是两层:.md5 与 .sha512 文件防「下载损坏」,归档目录同级的 KEYS 文件配合包旁的 .asc 签名文件可做 GPG 验签防「包被替换」。退役项目的发布渠道不再轮换密钥,GPG 流程与常规 Apache 项目一致(导入 KEYS 中对应发布经理的公钥后 gpg --verify),生产留档建议两层都执行,本文以校验和为准实测。

3.2 下载与校验和验证

实测下载与校验过程:

$ cd /opt/src
$ wget https://archive.apache.org/dist/lucy/apache-lucy-0.6.2.tar.gz
$ ls -l apache-lucy-0.6.2.tar.gz
-rw-r--r-- 1 root root 886072 apache-lucy-0.6.2.tar.gz

$ md5sum apache-lucy-0.6.2.tar.gz
a0aed8832917b38a43ba3b05c37b30e6  apache-lucy-0.6.2.tar.gz

$ sha512sum apache-lucy-0.6.2.tar.gz
911a8f73c5673802926816c346333244e94b8d9c47ef73768ea1501a38d8fecee53fb42047112feaff0febc549de78697e888336b31e4c0dff6b23920d171033  apache-lucy-0.6.2.tar.gz

与归档站点发布的官方校验文件逐字一致(官方 .md5 文件内容为 a0aed8832917b38a43ba3b05c37b30e6 apache-lucy-0.6.2.tar.gz,官方 .sha512 文件内容为 911a8f73...171033 apache-lucy-0.6.2.tar.gz),包完整性确认无误。

3.3 解压

$ cd /opt/src
$ tar xzf apache-lucy-0.6.2.tar.gz
$ ls apache-lucy-0.6.2
CHANGES  CONTRIBUTING  INSTALL  LICENSE  NOTICE  README  STATUS
appveyor.yml  c  common  core  devel  example-lang  go  lemon
modules  perl  ruby  tcl  test

解压后得到顶层目录,其中与 C 路线相关的只有三个:c/(C 库构建入口)、core/(语言无关的核心实现)、common/(charmonizer 配置探测工具);perl/、go/、ruby/、tcl/ 是各宿主语言绑定,本文不使用。

四、编译安装 Clownfish 编译器(cfc)

4.1 获取源码

Clownfish 没有独立的官方发行包,唯一可靠的来源是其 GitHub 仓库的 rel/v0.6.2 标签(与 Lucy 0.6.2 配套的版本)。实测克隆:

$ cd /opt/src
$ git clone --depth 1 --branch rel/v0.6.2 https://github.com/apache/lucy-clownfish.git lucy-clownfish
Cloning into 'lucy-clownfish'...
Note: switching to '6555ea20b0242f75914ee9567685cca15929a5bc'.

克隆得到的 commit 为 6555ea20b0242f75914ee9567685cca15929a5bc。仓库顶层包含 compiler/(编译器)与 runtime/(运行库)两个子目录,正好对应本章与下一章的两步。

4.2 配置与编译

$ cd /opt/src/lucy-clownfish/compiler/c
$ ./configure
Configuring Clownfish compiler...
Using C compiler 'gcc'
gcc ../common/charmonizer.c -o charmonizer
Running charmonizer
Initializing Charmonizer/Core/OperatingSystem...
Detected POSIX shell
Creating compiler object...
Trying to compile and execute a small test file...
Detected binary format: ELF
Detected make utility 'make'
Initialization complete.
Running BuildEnv module...
Running DirManip module...
Running Headers module...
Running FuncMacro module...
Running Booleans module...
Creating Makefile...
Cleaning up...
Cleanup complete.

$ make -j12

configure 阶段由 charmonizer 完成系统探测(编译器、ELF 格式、make 工具等),正常情况下十余秒内结束。make 编译产物为 compiler/c/cfc 可执行文件,实测大小 1,615,968 字节。编译过程中 flex 会被调用一次,用来自 ../src/CFCLexHeader.l 生成 ../src/CFCLexHeader.c。

charmonizer 是理解这套构建体系的钥匙,值得展开说明。它不是常规的 configure 脚本,而是一个「边探测边写代码」的配置探针:编译一个小 C 程序试运行,依次确认目标平台特性(回显里的 Running XX module 每一行就是一组探测——OS 特性、目录操作、头文件清单、函数式宏、布尔类型……),把确认结果写成 Charmonizer/Core 头文件注入构建。与 autotools 的 m4 宏展开相比,charmonizer 的探测直接以「能不能编译运行」为准绳,不依赖 m4 宏库;代价是探测项固定、不可扩展。三个构建入口(clownfish/compiler/c、clownfish/runtime/c、lucy/c)各自跑一遍 charmonizer,输出格式一致但探测深度递进——Lucy 的 configure 里多出的 Integers/Floats/Memory/大型文件等模块,正是检索引擎对整数宽度、浮点行为与内存操作更敏感的体现。把回显当作「平台适配报告」来读,遇到跨平台问题时每行 Running XX module 就是排查线索。

4.3 手动安装 cfc

实测确认:compiler/c 生成的 Makefile 没有提供 install 目标,make install 无效。安装方式是把编译好的二进制复制到 PATH 中:

$ cp /opt/src/lucy-clownfish/compiler/c/cfc /usr/local/bin/
$ cfc 2>&1 | head -1
Mandatory argument --dest missing

不带参数调用 cfc 得到 Mandatory argument --dest missing,带上未知参数(如 cfc --version)则得到 Invalid argument '--version'——两种回显都说明程序已在 PATH 中可执行(cfc 不提供版本参数,这一回显同时也验证了二进制可以正常运行)。

五、编译安装 Clownfish 运行库

5.1 配置与编译

$ cd /opt/src/lucy-clownfish/runtime/c
$ ./configure
(charmonizer 探测输出,与编译器阶段格式一致)
$ make -j12
(编译输出,最后两行为链接产物建立符号链接)
+ ln -sf libclownfish.so.0.6.2 libclownfish.so.0.6
+ ln -sf libclownfish.so.0.6 libclownfish.so

编译产物为 libclownfish.so.0.6.2 及两级 SONAME 符号链接。

5.2 执行安装脚本

runtime/c 目录自带安装脚本,实测可用(注意脚本文件名是 install,没有 .sh 后缀——虽然脚本内部 usage 函数自称 install.sh,属于上游自己的不一致):

$ ./install --prefix /usr/local

这个 135 行的 POSIX shell 脚本值得读一遍再执行,它的行为决定了安装形态:先解析 --prefix 参数并规范为绝对路径;按 uname 分平台安装库——Linux 分支为 cp libclownfish.so.0.6.2 到 prefix/lib 后建立两级符号链接(SONAME 级 libclownfish.so.0.6 与无版本级 libclownfish.so);随后把 autogen/share/* 整体复制到 prefix/share;最后补装 man 页(cp -R autogen/man/* "$prefix/man",复制的是内容而非目录本身)。对比 Lucy 同名脚本可以得出「Clownfish.cfp 遗漏」的源码级根因(5.3 节现象的解释):Lucy 的 install.sh 用 find ../core -name '*.cf[hp]' 从源码树复制(.cfh 与 .cfp 都覆盖,因此 Lucy.cfp/LucyX.cfp 齐备),而 Clownfish runtime 的安装脚本复制的是 autogen/share 下 cfc 生成的产物树——.cfp 清单不参与 cfc 代码生成,自然不在产物里。两个脚本对「类描述文件应该从哪来」的理解不同,这正是三段式构建各环节需要人工衔接的又一例证。

安装完成后,共享库进入 /usr/local/lib/,runtime/core/ 下的全部 .cfh 类描述文件进入 /usr/local/share/clownfish/include/Clownfish/。

5.3 补齐清单文件 Clownfish.cfp

实测发现 runtime 安装脚本的一个遗漏:Parcel 清单文件 Clownfish.cfp 不会被复制到 include 目录,需要手动补齐:

$ cp /opt/src/lucy-clownfish/runtime/core/Clownfish.cfp /usr/local/share/clownfish/include/

这一步不影响 libclownfish 本身的功能,但会让 include 目录的 Parcel 结构不完整(有 Clownfish 目录却缺同名 .cfp 清单)。为保持后续使用 cfc 处理用户自定义类时的环境一致,建议按上述命令补上。

六、编译安装 Lucy C 库

6.1 进入构建目录

Lucy 源码树的顶层没有 configure,C 库的构建入口在 c/ 子目录(该目录自带 configure 与 install.sh,结构上与 Clownfish runtime 相同):

$ cd /opt/src/apache-lucy-0.6.2/c

此构建会读取 ../core/ 下全部 153 份 .cfh 文件(实测 find ../core -name "*.cfh" | wc -l 输出 153),调用 /usr/local/bin/cfc 生成 autogen 头文件与引导代码,再编译链接。

cfc 的生成产物集中在构建目录的 autogen/ 下,形成五件套结构(实测):include/(头文件树)、source/(lucy_parcel.c 等引导源码与编译后的 .o)、share/(供 install.sh 安装的类描述树)、man/(生成的手册页,man3 分册实测 72 页)、以及 hierarchy.json(全部类的层级清单,Makefile 以它作为引导代码的依赖栅栏)。其中 man 页是容易忽略的赠品:install.sh 会把 autogen/man 装进 /usr/local/man,为第七章的离线 API 手册查询提供了基础。

6.2 配置

$ ./configure
(charmonizer 探测输出)
Running Integers module...
Running IntegerTypes module...
Running IntegerLimits module...
Running IntegerLiterals module...
Running IntegerFormatStrings module...
Running Floats module...
Running LargeFiles module...
Running Memory module...
Running RegularExpressions module...
Running VariadicMacros module...
Creating Makefile...
Cleaning up...
Cleanup complete.

configure 正常结束的标志是输出 Creating Makefile... 与 Cleanup complete.,随后回到 shell 提示符。

6.3 编译

$ make -j12

编译 12 核并发约两三分钟完成。最后一条链接命令(实测摘录,参数有省略):

gcc src/Lucy/Index/DocReader.o ... autogen/source/lucy_parcel.o \
  -shared -Wl,-soname,liblucy.so.0.6 -g -l m -l clownfish -o liblucy.so.0.6.2
ln -sf liblucy.so.0.6.2 liblucy.so.0.6
ln -sf liblucy.so.0.6 liblucy.so

可见 liblucy 以共享库方式链接了 m 数学库与 clownfish 运行库,SONAME 为 liblucy.so.0.6。同时还会编译测试库 libtestlucy.so.0.6.2(供 make test 使用,实测 2,146,952 字节)。产物构成的量化清单(实测):整棵源码树编译后共生成 254 个目标文件,其中 core/ 下 154 个(按 Makefile 的 ../core/%.o : ../core/%.c 规则逐文件编译)、c/src/ 下 4 个手工补充的 C 实现(Inverter、DocReader、Doc、RegexTokenizer——即 6.1 节提到的平台相关或 lemon 生成部分)、autogen/source 下 3 个 parcel 引导文件,其余为测试与工具目标。

值得注意的是 Makefile 的并行编译设计:core 的几百个类实现文件相互独立、可完全并行,而 autogen 引导文件依赖 hierarchy.json(由 cfc 汇总全部 .cfh 生成),构成一道同步栅栏——这正是 12 核编译时前段吃满、后段收尾的理论依据,也给手动并行参数选择提供了依据:core 阶段提高 -j 收益明显,引导与链接阶段则受限于单文件。

6.4 安装

$ ./install.sh --prefix /usr/local

install.sh 复制库文件到 /usr/local/lib/,并调用 cfc 把 ../core 的类描述安装到 /usr/local/share/clownfish/include/ 下的 Lucy/ 与 LucyX/ 目录,同时写入 Lucy.cfp 与 LucyX.cfp 清单。

七、验证安装

7.1 库文件与头文件布局

$ ls /usr/local/lib/liblucy* /usr/local/lib/libclownfish*
/usr/local/lib/libclownfish.so
/usr/local/lib/libclownfish.so.0.6
/usr/local/lib/libclownfish.so.0.6.2
/usr/local/lib/liblucy.so
/usr/local/lib/liblucy.so.0.6
/usr/local/lib/liblucy.so.0.6.2

$ ls /usr/local/share/clownfish/include/
Clownfish  Clownfish.cfp  Lucy  Lucy.cfp  LucyX  LucyX.cfp

三个 Parcel 目录加三份 .cfp 清单,与第一章 1.1 节的安装目标一致。

头文件之外还有一套离线 API 手册可用,但两个库的落点不同(实测):Lucy 的 72 页 lucy_* 手册在 /usr/local/share/man/man/man3/(install.sh 把 autogen/man 整目录复制进 $prefix/man,而本机的 /usr/local/man 是指向 share/man 的符号链接,物理路径解析后多嵌套了一层 man/),Clownfish 的 14 页 cfish_* 手册在 /usr/local/share/man/man3/(runtime 脚本复制 autogen/man/* 的内容)。另注意 Ubuntu 22.04 最小化安装的 man 程序本体可能被裁剪(实测执行 man 只返回 unminimize 提示),此时可直接读页面源文件(roff 格式),或补装 man-db 后按落点分别指定 -M 路径查阅:lucy 页用 man -M /usr/local/share/man/man lucy_Indexer(注意指向嵌套目录),cfish 页用 man -M /usr/local/share/man cfish_Err——遗产环境断网排障时比翻源码更快。

7.2 动态链接注册

Ubuntu 22.04 的默认链接器配置文件 /etc/ld.so.conf.d/libc.conf 已包含 /usr/local/lib,安装后执行一次 ldconfig 即可注册:

$ ldconfig
$ ldconfig -p | grep -E "lucy|clownfish"
        liblucy.so.0.6 (libc6,x86-64) => /usr/local/lib/liblucy.so.0.6
        liblucy.so (libc6,x86-64) => /usr/local/lib/liblucy.so
        libclownfish.so.0.6 (libc6,x86-64) => /usr/local/lib/libclownfish.so.0.6
        libclownfish.so (libc6,x86-64) => /usr/local/lib/libclownfish.so

7.3 运行官方测试套件

测试套件在构建目录(安装前)即可运行:

$ cd /opt/src/apache-lucy-0.6.2/c
$ make test
(依次输出各测试批次)
Running Lucy::Test::Search::TestSeriesMatcher...
Running Lucy::Test::Search::TestORQuery...
Running Lucy::Test::Search::TestQueryParserLogic...
Running Lucy::Test::Search::TestQueryParserSyntax...
65 batches passed. 5770 tests passed.
Result: PASS

65 个批次、5770 项断言全部通过,这是在 gcc 11.4 + glibc 2.35 的现代环境上得到的结论,说明 0.6.2 代码在较新工具链下依然健康。

这 65 个批次的组织方式同样值得读:批次名直接映射源码树的测试类(Running Lucy::Test::Search::TestSeriesMatcher... 对应 core/Lucy/Test/Search/TestSeriesMatcher.cfh 描述的测试类),覆盖从 Clownfish 对象模型、分析器、索引读写到查询解析的每一层。几个批次对理解引擎行为特别有价值:TestSeriesMatcher/TestORQuery 验证查询匹配器的组合语义,TestQueryParserLogic/Syntax 验证查询串到查询树的转换规则,索引批次则验证段写入与快照切换的事务性。改造分析器或修改索引逻辑后重跑 make test,是遗产系统维护中最有效的回归手段——5770 项断言就是 5770 个行为锚点。

而 make test 依赖的 libtestlucy 测试库(6.3 节实测 2.1MB)把全部测试类打进了一个可执行宿主:测试批次按序调用每个 Test 类的 Run 方法并汇总断言计数,任一批次失败即整体 Result: FAIL,退出码非零,可直接接入 CI。对退役项目而言,这套自包含测试体系的价值远超普通单元测试——它是在没有上游发版的环境里验证「我的修改没有破坏既有行为」的唯一官方手段。

7.4 编译运行官方示例

源码自带示例 c/sample/getting_started.c,它演示「建索引 + 三种查询」完整流程。实测以 /opt/lucy-demo 为工作目录:

$ mkdir -p /opt/lucy-demo && cd /opt/lucy-demo
$ cp /opt/src/apache-lucy-0.6.2/c/sample/getting_started.c .
$ cp -r /usr/local/share/clownfish/include cfh
$ CLOWNFISH_INCLUDE= /usr/local/bin/cfc --source=cfh --dest=autogen
$ gcc -I autogen/include -I autogen/include/Clownfish/v0.6.2 \
    getting_started.c -L/usr/local/lib -lclownfish -llucy -o getting_started
$ LD_LIBRARY_PATH=/usr/local/lib ./getting_started

程序正常运行并在 ./lucy_index 下生成索引目录,三种查询(单词、OR 组合、双引号短语)均返回正确命中,完整回显见本系列《Apache Lucy 0.6 全文检索实战详解》。这里有两个关键点:头文件生成时要把系统级 CLOWNFISH_INCLUDE 置空以避免与 cfh 目录冲突;gcc 需要同时给出 -I autogen/include 与 -I autogen/include/Clownfish/v0.6.2 两条 include 路径(详见第九章 FAQ 第 6 条)。

7.5 pkg-config 简化编译参数

install.sh 会生成 pkg-config 描述文件(实测 /usr/local/lib/pkgconfig/lucy.pc 内容):

Name: Apache Lucy
Description: Full-text search for dynamic languages
Version: 0.6.2
URL: http://lucy.apache.org/
Requires: clownfish
Libs: -L/usr/local/lib -llucy -lclownfish

配合 pkg-config 工具即可用一行拼装编译参数(注意 lucy.pc 不含 Cflags 项,include 路径仍需按 9.6 节手工给出;且 Ubuntu 22.04 最小安装默认不带 pkg-config,需 apt-get install -y pkg-config):

$ gcc getting_started.c $(pkg-config --libs lucy) -o getting_started

它解决的是库参数的维护问题:版本升级或改前缀后,Libs 行随 .pc 自动更新,工程里的链接命令不必跟着改。对只写一两个验证程序的读者,直接写 -l 参数更直观;把 Lucy 嵌入正式构建系统时,.pc 文件才是标准做法——这也正是上游把它写进 install.sh 的意图。

八、卸载与清理

Lucy 安装过程只写入固定路径,卸载即删除这些路径:

$ rm -f /usr/local/bin/cfc
$ rm -f /usr/local/lib/liblucy.so /usr/local/lib/liblucy.so.0.6 /usr/local/lib/liblucy.so.0.6.2
$ rm -f /usr/local/lib/libclownfish.so /usr/local/lib/libclownfish.so.0.6 /usr/local/lib/libclownfish.so.0.6.2
$ rm -rf /usr/local/share/clownfish
$ ldconfig

构建目录的清理:

$ rm -rf /opt/src/apache-lucy-0.6.2 /opt/src/lucy-clownfish /opt/src/apache-lucy-0.6.2.tar.gz

卸载是否彻底,建议按三步实测验证。第一步查链接器缓存,删除并重新执行 ldconfig 后应无任何输出:

$ ldconfig -p | grep -E "lucy|clownfish"
        liblucy.so.0.6 (libc6,x86-64) => /usr/local/lib/liblucy.so.0.6
        liblucy.so (libc6,x86-64) => /usr/local/lib/liblucy.so
        libclownfish.so.0.6 (libc6,x86-64) => /usr/local/lib/libclownfish.so.0.6
        libclownfish.so (libc6,x86-64) => /usr/local/lib/libclownfish.so

(以上为安装态的实测回显,注意缓存里登记的是 SONAME 级符号链接而非带完整版本号的文件——ldconfig 只收录 .so 结尾与 SONAME 形态的条目,这正是卸载时三个层级链接都要 rm 的原因。)第二步查 pkg-config 描述文件 ls /usr/local/lib/pkgconfig/,lucy.pc 与 clownfish.pc 应一并消失,否则残留会让其他工程的 pkg-config 探测报出不存在库的参数。第三步查头文件树 ls /usr/local/share/clownfish/include/,实测正常形态是 Clownfish、Lucy、LucyX 三个目录加齐备的 .cfp 清单,卸载后应整树不存在。若只删了库而留下 include 树,系统处于「半卸载」状态——编译能通过、链接必失败,排查时先核对这三处再怀疑代码。若只是想重新编译,删除源码树后重新解压即可,无需动 /usr/local。

九、FAQ 排障实录

9.1 没有 flex:configure 静默通过,make 才报错

这是本路线最容易踩的坑。实测在未安装 flex 的环境中执行 ./configure,退出码为 0,输出中完全不见 flex 字样,也无任何警告;直到 make 阶段才失败,实测报错:

lex  -t ../src/CFCLexHeader.l > ../src/CFCLexHeader.c
/bin/sh: 1: lex: not found
make: *** [<builtin>: ../src/CFCLexHeader.c] Error 127

原因:Clownfish 编译器的 Makefile 通过内建规则调用 lex(flex 提供的命令名)从 CFCLexHeader.l 生成词法分析器源码,lex 不存在则生成失败,Error 127 即命令未找到。解决:apt-get install -y flex 后重新执行 configure 与 make。

9.2 cfc 编译后如何安装?没有 make install

compiler/c 的 Makefile 实测不存在 install 目标,这是上游的设计而非缺陷——cfc 只是一个独立可执行文件。处理方式即第四章 4.3 节:cp compiler/c/cfc /usr/local/bin/。

9.3 Clownfish.cfp 不会被安装脚本复制

runtime/c 的安装脚本只安装 autogen/share 下的 cfc 生成产物与库文件,Parcel 清单 Clownfish.cfp 需要手动补入 include 目录(第五章 5.3 节,根因分析见 5.2 节的脚本对比)。判断依据:安装后 ls /usr/local/share/clownfish/include/ 中 Clownfish 目录存在而 Clownfish.cfp 缺失,与 Lucy/LucyX 两个 Parcel 「目录 + .cfp」齐备的形态不一致。

9.4 运行程序报 error while loading shared libraries

编译出的程序在运行时报 error while loading shared libraries: liblucy.so.0.6: cannot open shared object file,原因是共享库未注册进链接器缓存。Ubuntu 22.04 默认配置已含 /usr/local/lib,执行一次 ldconfig 即可;若不希望全局注册,可在运行时前缀 LD_LIBRARY_PATH=/usr/local/lib。

9.5 cfc --dest 指向不存在的多级目录时报 Couldn't create directory

实测让 cfc 向一个全新的目录生成头文件时,无论 dest 路径是否预先 mkdir 顶层,都可能出现类似报错:

Couldn't create directory '/tmp/ag-test/include/Clownfish/v0.6.2/Clownfish'

原因是 cfc 内部的目录创建不是递归 mkdir。解决:预先用 mkdir -p 建好目标树,或如第七章 7.4 节那样直接在目标目录中先复制一份完整的 include 树再生成。

9.6 为什么编译用户程序需要两条 -I 路径

cfc 生成的头文件分布在两处:autogen/include/ 下有 parcel 引导头(如 lucy_parcel.h)与部分头文件,而 autogen/include/Clownfish/v0.6.2/ 下按「Parcel 名/版本号」存放同一批头文件的另一份布局。实测两种位置都会被 #include <Clownfish/String.h> 这类语句引用到,缺任何一条路径都会报 fatal error: Clownfish/xxx.h: No such file or directory。这是 Clownfish 版本化 include 设计带来的固定写法,两条 -I 缺一不可。

9.7 下载慢,或常规分发站点 404

Lucy 是退役项目,实测 dlcdn.apache.org 的 /dist/lucy/ 返回 404,只能从 archive.apache.org 下载;归档站点响应头带 Vary: Slow,Glacial,速度限制是官方刻意行为。866 KB 的包也可能需要等待较久,无需换源重试。

9.8 编译告警很多,是否正常

正常。实测 Makefile 的 CFLAGS 为 -pedantic -Wall -Wextra -Wno-variadic-macros -Wno-overlength-strings -std=gnu99 -D_GNU_SOURCE -O2 -g,上游主动豁免了变长宏与超长字符串两类告警,其余告警(如未使用参数)在 gcc 11 下会比 2018 年的构建环境更多,均不影响产物。只要最终出现 ln -sf liblucy.so.0.6.2 liblucy.so.0.6 并生成库文件即为成功。

9.9 INSTALL 文件只提到 Perl,是不是装错了包

没装错。0.6.2 的 INSTALL 原文只描述 Perl 绑定路线(第三章已摘录),C 路线是源码树自带但未写入 INSTALL 的构建入口(顶层 c/ 目录)。这也解释了为什么本路线的步骤需要手工衔接:Clownfish 编译器、Clownfish 运行库、Lucy C 库三者的构建系统彼此独立。

9.10 make test 应该在安装前还是安装后运行

实测两者皆可。make test 依赖构建目录中的 libtestlucy 测试库,在 c/ 目录内直接运行,不读取 /usr/local 下的安装产物;本文按「先安装、后测试」的顺序呈现,仅是为了与验证章节连贯。若测试失败,可放心先排查环境再决定是否保留安装产物。

9.11 需要多少磁盘与编译时间

实测在 12 核环境下:Clownfish 编译器约 1 分钟,Clownfish 运行库不足 1 分钟,Lucy C 库约 2-3 分钟,make test 约 2 分钟,全程合计 10 分钟内。峰值磁盘占用(源码树 + 构建中间产物)约 250 MB。

9.12 无外网环境如何完整构建

Lucy 的源码包内嵌全部核心源码,唯一的外部依赖是 Clownfish(git 拉取)。离线环境先在联网机执行 git clone(或从 GitHub 下载 rel/v0.6.2 标签的 zip 包),把 clownfish 目录整体随 Lucy 源码包分发到内网即可,构建过程(charmonizer 探测、cfc 生成、编译链接)全程不需要网络。校验和文件与 KEYS 也一并预取,保证离线留档的供应链可审计。

9.13 嵌入场景想减小库体积,能 strip 吗

可以。实测 liblucy.so.0.6.2 为 6,450,800 字节(-g 编译含调试信息),复制一份执行 strip --strip-unneeded 后为 1,968,696 字节,缩至约三成且功能不受影响。权衡点在于符号级排障能力:保留未 strip 版本可在崩溃时用 gdb 定位到源码行,生产嵌入建议「留档未 strip 副本 + 部署 strip 后副本」。

9.14 构建目录与源码目录是什么关系,改代码后如何重编

Lucy 的 C 构建是「半树内」模式:configure 在 c/ 目录内生成 Makefile,但目标文件分散落在源码树各处(core/ 下与源码同名、autogen/source 下为引导代码),没有独立的 build 目录。因此修改 core 下某个 .c/.cfh 后,直接在 c/ 目录 make 即可增量重编;修改 .cfh 时注意 make 会先经 cfc 重新生成对应的 autogen 头文件再编译(hierarchy.json 依赖链保证这一点),无需手工触发 cfc。若生成链路状态混乱(如 9.5 节的目录问题),删除 autogen/ 后重新 configure 是最干净的重置方式。