关于自动生成autoloads的方法

这个问题有过相关讨论:

不过我在尝试使用update-directory-autoloads时遇到了问题。

我的emacs.d目录结构如下:

...
├── git-pkg/
├── init.el
├── lisp/
├── loaddefs.el
...

loaddefs.el 里的autoloads是从git-pkg/lisp/中生成的,其中一个例子是:

(autoload 'my-new-line "lisp/init-commands" "\


\(fn)" t nil)

执行命令my-new-line,会报错:

Cannot open load file: No such file or directory, lisp/init-commands

在此之前,loaddefs.ellisp/目录下的,且只有lisp/下文件的autoload,也是可以正常使用的。

如果修改lisp/init-commands~/.emacs.d/lisp/init-commands或者init-commands也是可以正常使用命令的。

目标文件要用绝对路径。你可以参考我那个函数的第一个cl-loop里面的内容

不一定非要绝对路径,可能是load-path的问题?

可以不用绝对路径,init-command.el所在目录在load-path里就行,直接写init-command就可以,load会搜索load-path里的目录。

我之前不知道可以通过generated-autoload-load-name来修改autoload-file里的名字,但是这样也没法用update-directory-autoloads了,因为对每个文件我都要处理它的文件名:

(file-name-base "~/.emacs.d/lisp/init-commands.el") => “init-commands”

(defun my--update-loaddefs ()
  (interactive)
  (let* ((generated-autoload-file (locate-user-emacs-file "loaddefs.el"))
         (load-path-regex "\\.emacs\\.d\\/\\(lisp\\|git-pkg\\)\\/")
         (load-path (seq-filter (lambda (dir)
                                  (string-match-p load-path-regex dir))
                                load-path)))

    (with-current-buffer (find-file-noselect generated-autoload-file)
	  (insert ";;")
	  (save-buffer))

    (dolist (lp load-path)
      (let ((files (directory-files-recursively lp ".el$")))
        (dolist (f files)
          (let ((generated-autoload-load-name (file-name-base f)))
            (update-file-autoloads f t)))))))

(add-hook 'kill-emacs-hook #'my--update-loaddefs)
(load (locate-user-emacs-file "loaddefs.el") nil t )

我还发现autoload里有相关函数的行为很怪异,不知道是我没理解它的文档描述,还是有bug。不过我的问题算是解决了。

这个我还不清楚,可以试试看

我最初就是这样的实现,可能确实需要这样做才行

或许可以参考一下我之前写的 maple-package

我也是参考doom-emacsload-path里的所有package autoloads.el提取成一个文件,然后load it

可以let-boundgenerated-autoload-load-name指定生成autoload时候的名字

(let ((generated-autoload-load-name (file-name-sans-extension (file-name-nondirectory f))))
          (autoload-generate-file-autoloads f (current-buffer)))

才发现楼主已经提过了… 瞎了瞎了 :sob:

分享下自己的

(defun cm/find-subdir-recursively (dir)
  "Find all subdirectories in DIR.

Dot-directories and directories contain `.nosearch' will be skipped."
  (thread-last (directory-files dir nil)
    (cl-remove-if (lambda (f)
                    (string-prefix-p "." f)))
    (mapcar (lambda (d) (expand-file-name d dir)))
    (cl-remove-if-not #'file-directory-p)
    (cl-remove-if (lambda (d)
                    (file-exists-p (expand-file-name ".nosearch"
                                                     d))))))

(defun cm/find-el-file-recursively (dir)
  "Find all `.el' files in DIR and its subdirectories."
  (let ((elfiles (directory-files dir t "\\.el\\'"))
        (subdir (cm/find-subdir-recursively dir)))
    (nconc elfiles
           (mapcan #'cm/find-el-file-recursively subdir))))

(defun cm/generate-autoloads (&optional dir target)
  "Generate autoload files recursively for all package in DIR to file TARGET.

If DIR is omitted, use `cm/site-lisp-directory' as DIR, if target is ommitted
use `cm/autoloads-file' as TARGET."
  (interactive)
  (require 'autoload)
  (let* ((target (or target cm/autoloads-file))
         (dir (or dir cm/site-lisp-directory))
         (generated-autoload-file target))
    (with-temp-file target
      (dolist (f (cm/find-el-file-recursively dir))
        (let ((generated-autoload-load-name (file-name-sans-extension f)))
          (autoload-generate-file-autoloads f (current-buffer))))
      (insert (string-join `(,(char-to-string ?\C-l)
                             ";; Local Varibles:"
                             ";; version-control: never"
                             ";; no-byte-compile: t"
                             ";; no-update-autoloads: t"
                             ";; coding: utf-8"
                             ";; End:"
                             ,(format ";;; %s ends here"
                                      (file-name-nondirectory target)))
                           "\n")))
    (load target :no-error :no-message)))

忽略点文件夹是因为没有必要,还能加快搜索速度。

忽略含有.nosearch文件的文件夹。是因为flycheck的test里面藏了一些有语法错误的elisp文件,然后人家用.nosearch文件防止被搜索。直接用directory-files-recursively的话可不会管那么多,然后你的autoload生成函数去扫描这些malformed的文件的时候就跪了。

1 个赞

这个函数我记得有一个参数就是导出的文件名啊

这个变量指定的是写在loaddef文件里。注册autoload时指定的目标函数所在文件名

(autoload 'aggressive-indent-indent-defun "/home/chino/.emacs.d/lib/site-lisp/aggressive-indent-mode/aggressive-indent" "\
Indent current defun.
Throw an error if parentheses are unbalanced.
If L and R are provided, use them for finding the start and end of defun.

\(fn &optional L R)" t nil)

我记错了,我用的是这个函数update-file-autoloads。看起来是autoload-generate-file-autoloads的一个封装。

update-file-autoloads现在是不是deprecate了,整个autoloads包都deprecate了好像,现在是loaddefs.el 是吧

現在用 loaddefs-generate

这个方法是不是 不需要手动调用的啊?

因为好像需要(require 'loaddefs) 才加载到这个方法

但感觉又不是Emacs自动调用的,因为我下载了第三方包,手动配置,没有通过use-package,

它好像没有给我生成loaddefs.el 文件


update: 通过 M-x locate-library RET loaddefs 好像有这个loaddefs.elc

但是好像没有构建我下载下来的第三方包的 autoload ,我还以为在Emacs启动的时候会在load-path下自动执行这个loaddefs-generate ,好像不是那么一回事

(use-package core-autoloads
  :load-path "core"
  :init (loaddefs-generate (concat user-emacs-directory "core")
                           (concat user-emacs-directory "core/core-autoloads.el")))

这样用。loaddefs-generate的两个参数分别是你放需要生成autoload的文件的目录,第二个参数是生成的autoload文件的路径。然后你需要load这个文件。

上面这个方法在每次emacs启动时都会生成一次autoload文件。

用 autoload 生成本就是为了节约时间不用把所有文件扫描解析一遍,每次启动都做岂不是本末倒置了

1 个赞