PAYATHON 2026

ASDF:REQUIRE-SYSTEM 已弃用,是否应完全避免使用?

技术老齐

结论

新代码应使用 asdf:load-system,避免使用 asdf:require-system。如果某个 system 属于项目依赖,应在 .asd 文件的 :depends-on 中声明。

除了弃用警告,asdf:require-system 还可能让当前 Lisp 会话继续使用过期代码。它把“已经加载”作为终止条件,再次调用时可能不再检查该 system 及其依赖是否发生变化。开发镜像长时间运行,或者期间更新了 Quicklisp dist、切换了 Git 分支、修改了依赖源码,都可能遇到这个问题。

require-system 会怎样

调用:

(asdf:require-system :str)

第一次通常能正常加载 system。此后,只要 ASDF 认为它已经加载,再次调用就可能直接返回,不再规划加载操作,也不会更新其依赖。

例如,在同一个 Lisp 进程中依次执行:

  1. 使用 asdf:require-system 加载 str。
  2. 更新 str 的源码,或者更新它依赖的 system。
  3. 再次调用 asdf:require-system。

第三步可能不会执行任何加载操作,当前镜像仍然使用旧定义。这个问题不一定马上显现,因为磁盘上的代码已经更新,内存中运行的却还是旧版本。

改用:

(asdf:load-system :str)

ASDF 会按正常机制检查操作状态和依赖关系。没有变化时,它本来就不会无故重新编译或加载所有文件,因此无须用 require-system 来减少重复工作。

如何声明依赖

如果项目代码会调用 cl-str,str 就是项目依赖,与开发者是否打算修改它无关。应在 system 定义中声明:

(asdf:defsystem "my-project"
  :depends-on ("str")
  :components
  ((:file "package")
   (:file "main" :depends-on ("package"))))

之后只需加载项目自己的 system:

(asdf:load-system "my-project")

ASDF 会加载 str 及其传递依赖。依赖信息保存在 .asd 文件中,其他开发者、构建环境和部署工具也会使用同一套定义。

如果某个库只用于测试,可以将它放入单独的测试 system:

(asdf:defsystem "my-project/tests"
  :depends-on ("my-project" "fiveam")
  :components
  ((:file "tests")))

这种方式比在 REPL 启动脚本中手动调用 require-system 更容易维护。

使用 Quicklisp 时,也可以在交互环境中执行:

(ql:quickload "my-project")

Quicklisp 负责查找或安装缺失的软件,构建和加载仍由 ASDF 完成。

不要按是否修改源码选择加载函数

asdf:load-system 和 asdf:require-system 不能用来区分“正在开发的 system”与“只使用、不修改的 system”。

是否修改源码只是开发者当前的工作安排,并非可靠的依赖属性。即使不直接编辑 str,它仍可能因以下情况发生变化:

  • Quicklisp 或其他包管理来源更新了版本;
  • 工作区、Git 分支或提交发生切换;
  • 它依赖的某个 system 发生变化;
  • .asd 文件或编译环境发生变化;
  • 同一 Lisp 镜像已经运行了较长时间。

项目依赖应通过 :depends-on 表达。system 位于本地开发目录还是来自 Quicklisp,则由源码注册表和开发环境管理。

与 cl:require、cl:*modules* 的关系

不能把 asdf:require-system 的弃用简单归因于 cl:require 的弃用。两者是不同的 API,但都采用一次性加载思路,即模块已经提供后便不再处理。这个模型无法提供 ASDF 构建系统需要的细粒度依赖和更新检查,不适合用于现代 ASDF 项目的日常加载。

在 SBCL、SLIME 环境中,cl:*modules* 可能包含 ASDF、UIOP、SWANK-* 或 SBCL 扩展模块。这并不意味着应用代码也应采用同一机制。实现和开发工具可能仍保留 require/provide 兼容接口,而 cl:*modules* 只是已提供模块名称的记录,无法代替 ASDF 的 system 定义、依赖图和构建状态。

如何处理现有代码

可以直接替换:

;; 旧写法
(asdf:require-system :str)

;; 推荐写法
(asdf:load-system :str)

如果 str 是另一个 system 的正式依赖,还应将它移入 :depends-on,并删除业务代码或启动脚本中的手动加载调用。

旧程序暂时保留 asdf:require-system 通常不会立即出错,但会持续出现弃用警告,同一会话中也可能继续使用过期定义,将来升级 ASDF 时还可能遇到兼容性问题。明确依赖一次性加载语义的遗留环境可以在了解这些限制后继续使用,普通项目则没有必要保留它。

ASDF 的相关建议可参阅其最佳实践文档。

备注:内容仅供参考。