关于自动生成autoloads的方法


#1

这个问题有过相关讨论:

不过我在尝试使用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也是可以正常使用命令的。


#2

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


#3

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


#5

可以不用绝对路径,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。不过我的问题算是解决了。


#6

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


#8

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


#9

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

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


#10

可以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的文件的时候就跪了。


#11

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


#12

这个变量指定的是写在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)

#13

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