非程序员写的 macOS 版 Emacs 构建脚本

:rofl: 感谢提醒。

好在这个脚本不干别的事,GitHub repo 默认名称中不允许空格。

mclear动作挺快,合上了。

刚把source-directory和find-function-C-source-directory 改了一下,现在xref 可以查询c source了。helpful的 references也能显示c source. 我提供了两个source, 一般情况下是一致的,倾向使用与emacs.app版本一致的/Resources/源,如果由于任何原因没有复制成功,那就使用emacs-git里的/src/。

(let* ((emacs-Res "/Applications/Emacs.app/Contents/Resources/") ; same as current emacs build
       (emacs-git "/Users/z/Developer/building-emacs/emacs-git/") ; emacs-git /src/ source
       (emacs-src (if (file-exists-p (concat emacs-Res "src/")) emacs-Res emacs-git)))
  (setq source-directory emacs-src)
  (setq find-function-C-source-directory (concat emacs-src "src/")))
1 个赞

你不说我还真没注意,默认的 source-directory 居然是 tmp/emacs-build

嗯,你改成自己的路径就好了。

其实只要设置source-directory 就足够了,find-function-C-source-directory 最终也是找source-directory,见源码↓ 我就是上个双保险。

(defvar find-function-C-source-directory
  (let ((dir (expand-file-name "src" source-directory)))
    (if (file-accessible-directory-p dir) dir))
  "Directory where the C source files of Emacs can be found.
If nil, do not try to find the source code of functions and variables
defined in C.")
1 个赞

为双保险点赞 :+1:

被动接受不如主动出击控制,哈哈。

let* form 那俩也是双保险了一下,毕竟总有出错的时候,但总要有一个 /src 能用。 :rofl:

另外,之前一直不想上native-compile, 今天编译的时候加上了,启动时间从平均12-14s 来到了6-7s多。虽然一直调侃native-c 作者论文答辩完就没后续了,但真香。

:laughing: 俺也一样,感觉 NC 了以后肉眼可见的快了很多。

今天停掉 borg 模块,启动时不再检查load-path和autoload(310个包)了,启动时间从7到2.5,好家伙。

再请教个问题:不知道你用不用 emacs-rime,我现在emacs-rime 找不到一个emacs-module.h的文件,狗哥曾经在一个issue里回答说它是伴随着编译生成的 (Compile Rime dynamic module failed · Issue #141 · DogLooksGood/emacs-rime · GitHub) 。我检查了下 /src 文件夹里有个emacs-module.h.in,虽然内容是一样的,可以手动复制到emacs-module.h,但我好奇这个 build script 有没有自动生成。(如果没用 rime 就别测试了,rime挺麻烦的。

我没有用 RIME,用的是 macOS 自带输入法、pyim、sis。 :laughing:

1 个赞

请问你说的是哪个 borg 模块?

是这样的,我最近重构了emacs config, 把borg里的所有的 load-path 和 autoload 都捞出来放到各自文件里,然后直接在init.el 加载这些autoloads,只会按需加载。

再把borg的几个常用命令,-assimilate/remove/build 都autoload一下(因为它没有提供任何autoload, 手动autoload下后面才能使用)

这样启动的时候borg相关的东西都不会运行。borg 本身initialize会花不少时间,自身会把help模块挂起来,我的help模块又挂着helpful 和elisp-demos, helpful又挂着ol,死亡循环。虽然有个delay-mode-hooks可以使用,但我现在还没整明白怎么delay, 只好绕过borg。

补充:我个人配置里也写了很多defun, 集中到各种 lib-*.el 文件里:比如 lib-completion.el,lib-buffer.el,lib-project.el,lib-telega.el等等,这些文件都没有require, 而是会被收集到那个统一的autoloads.el里。

Borg 本身就是安装好包并在包的目录下生成了 autoload 文件和 texi 文档,加载不至于要那么多时间。我用 Borg 都是秒启动的。 Borg 安装好的包都是 autolaod 好的,可以像其他内置的包一样使用。

关于延时加载,我是用 use-package,设置起来更加方便。而且 Emacs 29 已经内置了 use-package, 不需要单独安装。

你用了 auto-compile 这个包吗?这个包会检查3次 load-path,会很慢。

检查三次load-path是我没有想到的 :rofl: 不过我只挂到emacs-lisp-mode-hook上,倒也不影响。 印象中收集load-path我的老机器大约2秒多,我包比较多,300+

主要是我需要收集我个人配置 lib-*.el 的autoloads, 所以干脆就全部自己管理了。

我刚从use-package跳出来,然后又嫌弃with-eval-after-load 太长太丑,折腾了一下setup,目前除了深水区的org/roam/vulpea/citar/bib超级模块,基本转换完成了(前几天传来消息说setup作者要把包转手了…还好包不太复杂。)

包太多就不要用 auto-compile 这个包了。

都喜欢自己动手的话可以像懒猫大佬那样,直接用 git submodule ,Borg 也没必要了。
这是他的配置,可以参考下

2 个赞

感觉 borg 就是个简化版的 submodule 流程,不如用 git/magit 管理来的直接。

搭配猫大的这个函数,就不用每次都 add-to-path,只需要 require 就可以用了。

;; package: add other source packages to load path
;; {{{
;; folder
(require 'cl-lib)
(defun add-subdirs-to-load-path (search-dir)
  (interactive)
  (let* ((dir (file-name-as-directory search-dir)))
    (dolist (subdir
             ;; 过滤出不必要的目录,提升 Emacs 启动速度
             (cl-remove-if
              #'(lambda (subdir)
                  (or
                   ;; 不是目录的文件都移除
                   (not (file-directory-p (concat dir subdir)))
                   ;; 父目录、 语言相关和版本控制目录都移除
                   (member subdir '("." ".."
                                    "dist" "node_modules" "__pycache__"
                                    "RCS" "CVS" "rcs" "cvs" ".git" ".github"))))
              (directory-files dir)))
      (let ((subdir-path (concat dir (file-name-as-directory subdir))))
        ;; 目录下有 .el .so .dll 文件的路径才添加到 `load-path' 中,提升 Emacs 启动速度
        (when (cl-some #'(lambda (subdir-file)
                           (and (file-regular-p (concat subdir-path subdir-file))
                                ;; .so .dll 文件指非 Elisp 语言编写的 Emacs 动态库
                                (member (file-name-extension subdir-file) '("el" "so" "dll"))))
                       (directory-files subdir-path))

          ;; 注意:`add-to-list' 函数的第三个参数必须为 t ,表示加到列表末尾
          ;; 这样 Emacs 会从父目录到子目录的顺序搜索 Elisp 插件,顺序反过来会导致 Emacs 无法正常启动
          (add-to-list 'load-path subdir-path t))

        ;; 继续递归搜索子目录
        (add-subdirs-to-load-path subdir-path)))))

(add-subdirs-to-load-path "~/.config/emacs/lib")

;; file
(defun add-files-to-load-path (folder)
  "Add FOLDER and its subdirectories to `load-path'."
  (let ((base folder))
    (unless (member base load-path)
      (add-to-list 'load-path base))
    (dolist (f (directory-files base))
      (let ((name (concat base "/" f)))
        (when (and (file-directory-p name)
                   (not (equal f ".."))
                   (not (equal f ".")))
          (unless (member base load-path)
            (add-to-list 'load-path name)))))))

(add-files-to-load-path (expand-file-name "site-lisp" user-emacs-directory))
;; }}}

这个函数load单个包还行,load所有包耗时要花不少时间,还不如把load-path写到外置文件,启动的时候load一下。

我目前只有 14 个用 git submodule 管理的包,没感觉耗时多少,猫大管理的包很多啊,都用好多年了,应该没什么很大的性能问题吧。

要配合他的lazy-load一起用,不然启动速度很慢。

刚老机器测试了一下,310个包,收集了476 个path,耗时1.055秒。 倒不是很贵。 @suliveevil

(require 'benchmark)
(benchmark-elapse
 (add-subdirs-to-load-path user-emacs-directory))
1 个赞

lazy-load 我没看懂,就没抄进我的配置。

我现在是把配置分成了三块:Emacs 配置,elpa/melpa 包配置,submodule 包配置,用的包比较少,还没发现什么性能问题。