最近写配置用到了autoload
,之前一直不是特别理解,今天查了一些资料还是没太搞清楚,赶紧来社区问一下大佬们。
最早看子龙山人的视频里说autoload
需要加对应的配置语句类似(autoload functionA fileA)
,这样启动时可以不加载fileA
,而调用functionA
时才去加载fileA
,并且也不需要去显式require
这个fileA
。为了避免写太多类似配置语句,可以使用魔法注释;;;###autoload
直接放到对应配置文件的函数或变量前,emacs在init时会自动去load-path
里收集这些autoload
的信息。
我的疑问是:
1、我自己定义的配置文件给某些函数加了魔法注释,该文件也在load-path
里,但为什么启动emacs后M-x找不到这个函数;
2、使用use-package
加载这个文件,配置了:defer t
,效果同上,难道是我缺少了哪个步骤吗,还是我对autoload
的理解就不对?
1 个赞
cireu
2020 年11 月 7 日 05:23
2
我们已经知道 autoload
函数可以创建 autoload 对象,但为了让autoload能让开发者和
用户用起来更贴心,下列两个问题急需解决。
手动维护autoload定义十分麻烦,增删函数时容易忘记同时更新autoload定义。
缺乏机制来保证autoload定义文件在包加载前就被加载到用户的Emacs实例内。
对于问题一,Emacs引入了magic comment(魔法注释) ;;;###autoload
如下示
;;;###autoload
(defmacro cl-incf (place &optional x)
"Increment PLACE by X (1 by default).
PLACE may be a symbol, or any generalized variable allowed by `setf'.
The return value is the incremented value of PLACE."
(declare (debug (place &optional form)))
(if (symbolp place)
(list 'setq place (if x (list '+ place x) (list '1+ place)))
(list 'cl-callf '+ place (or x 1))))
一个注释本身并不能掀起什么波澜。但当与配套的工具使用时,这将大大简化更新autoload
定义的流程。对此,Emacs 提供了 autoload.el
。 它负责从源代码的magic comment中生
成真正的autoload定义。
到现在,我们可以理清楚Emacs的Autoload机制是如何和包管理器(package.el
)结合服务
于用户了:
package.el
使用 update-directory-autoloads
从安装的包中提取 autoload文件,
提取出来的文件命名为 <PKGNAME>-autoloads.el
。
package-initialize
搜索所有已经安装的包,设置全局变量 load-path
并加载所
有的 <PKGNAME>-autoloads.el
。
用户在配置里直接使用被 autoloaded 的函数(比如,用 add-hook
添加函数到钩子
里。实际运行的代码在函数被调用时触发加载。实现懒加载机制。
引用自本人一篇未发布博文
2 个赞
不好意思,没太看出问题,我是要把配置文件的PATH赋给哪个变量吗?
cireu
2020 年11 月 7 日 05:37
4
就是你要用update-directory-autoloads
生成autoload定义文件, 然后在配置里load这个文件. 纯写注释没卵用
哦哦我好像明白点了,我删掉了一些之前抄的和package.el
相关的配置,里面应该有update-directory-autoloads
相关的内容,也就是说需要手动加上。
写 package 的时候才用 ;;;###autoload
,安装包的时候会自动生成 xxx-autoloads.el,里面会把这些注释转换成 (autoload ....)
调用,Emacs 启动时会自动加载所有的 xxx-autoloads.el 文件。
自己配置的时候,直接写 (autoload ....)
,use-package 的 :commands 就是如此干的。
嗯嗯,自己写的包加了魔法注释,需要手动生成对应的xxx-autoload.el,然后在init.el里require它。
chiron
2020 年11 月 7 日 08:03
10
可以参考abo-abo 的回答
libraries
我是把自定义的函数都写在一个文件里, 比如 chiron-functions.el, 函数前加 ;;;###autoload
然后执行一下
(update-file-autoloads "chiron-functions.el" t (expand-file-name "chiron-functions-autoloads.el"))
在配置文件里 (require 'chiron-functions-autoloads)
期待发布。
这两天在研究手动管理 package 的方式,稍微研究了下 autoload/load-file/require/use-package 之间的关系,之前一直在抄配置,没太理解。
贴几个觉得有用的链接:
1 个赞
kinono
2020 年11 月 7 日 13:22
13
贴一段在我这里一直可靠工作的代码(不少东西是从 straight.el
抄来的):
;;;; Site-lisp
;; The site-lisp directory is where we put our own packages. We byte-compile
;; and generate an autoload file for them. We only do this when a package is
;; newer than its byte-compiled version.
;; This is needed, or `generated-autoload-file' will be not defined as a
;; variable at byte-compile time. See the comments in
;; `straight--generate-package-autoloads'.
(eval-and-compile
(require 'autoload)
(require 'bytecomp))
(let* (;; Dir & files
(site-lisp-dir (concat user-emacs-directory "site-lisp/"))
(build-dir (progn (make-directory (concat site-lisp-dir ".build/") t)
(concat site-lisp-dir ".build/")))
(build-files (directory-files build-dir))
(newer-lisp-file nil)
;; Don't bother me.
(inhibit-message t)
;; Prevent `update-directory-autoloads' from running hooks when visiting
;; the autoload file.
(find-file-hook nil)
(write-file-functions nil)
;; Prevent `update-directory-autoloads' from creating backup files.
(backup-inhibited t)
(version-control 'never)
(generated-autoload-file (concat build-dir "autoloads.el")))
(cl-letf (((symbol-function #'byte-compile-log-1) #'ignore)
((symbol-function #'byte-compile-log-file) #'ignore)
((symbol-function #'byte-compile-log-warning) #'ignore))
(add-to-list 'load-path build-dir)
(dolist (file (directory-files site-lisp-dir))
(unless (string-prefix-p "." file)
;; Make symlinks of site-lisp files in build-dir. This is needed for
;; `byte-compile-file' and `update-directory-autoloads'.
(unless (member file build-files)
(make-symbolic-link (concat site-lisp-dir file)
(concat build-dir file)))
;; Byte compile
(let ((byte-file (concat build-dir
(file-name-sans-extension file)
".elc")))
(when (file-newer-than-file-p (concat site-lisp-dir file) byte-file)
(setq newer-lisp-file t)
(byte-compile-file (concat build-dir file))))))
;; Generate autoload file
(when newer-lisp-file
(unless (file-exists-p generated-autoload-file)
(with-current-buffer (find-file-noselect generated-autoload-file)
(insert ";; -*- lexical-binding: t -*-\n")
(save-buffer)))
(update-directory-autoloads build-dir)
(byte-compile-file (concat build-dir "autoloads.el")))
;; Load autoload file
(load (concat build-dir "autoloads") 'noerror 'nomessage)))
它会:
自动扫描 .emacs.d/site-lisp/
下的 .el
文件,然后把它编译到 .emacs.d/site-lisp/.build/
下面。
收集这些文件里加了 ;;;###autoload
的定义,创建.emacs.d/build/autoloads.el
并且自动加载。
把 .emacs.d/site-lisp/.build/
加到 load-path
,这样就可以用 require
或 use-package
了。
按需更新,在 site-lisp
下有任何 .el
文件有改动的情况下才会重做整个流程,平时几乎不耗时。
6 个赞