讨论一下Emacs半手动包管理

我本来用的是 package.el 和 quelpa 组合的方式管理包;仿照doom-emacs的形式,用make file安装/更新包。package.el 和 quepla 的组合有这么几个痛点:

  1. 包更新的函数基本上是一个大hack。
  2. 一边使用一边开发包很麻烦

因为 2. 我尝试了straight.el,和el-get,问题:

  1. 系统都比较复杂,尤其是el-get,我看了一周末才搞清楚怎么用
  2. 都有几个包装不上,因为系统复杂,我死活搞不清楚怎么回事
  3. straight.el不能shadow clone,而且org-mode还有bug

懒猫说他都是手动管理包,一开始我觉的这得多麻烦啊,不过实际上这样有几个好处(对我来说)

  1. 直接git clone,一边开发一边用就很方便
  2. 我的配置承担了autoload和load-path的配置,我不需要其他包管理器的这些功能,我只要把包下下来就够了
  3. 不用跟包管理器较劲,减轻心智负担
  4. 配置少一个外部依赖
  5. 可以shadow clone

我觉得这个吼的呀,我不用像懒猫一样纯手动用git submodule(工作量有点大),我可以写个小函数帮我把所有包直接clone下来。然后就完了,so easy。

唯一有点麻烦的是记录所有我用到的包的recipe,不过很快就写完了。

结果是cowboy.el,一共有三个函数:cowboy-install cowboy-update cowboy-delete。一共一百多行代码吧,很简单但是用起来还行。目前只支持github。

这里是代码:cowboy.el

你们对半手动管理包怎么看?我觉得这种方法唯一的缺点可能是不能支持很复杂的build,我的计划是太复杂的就直接手动装。这样的话可以尽量把包管理简单化和透明化,出问题也好解决。

2 个赞

我一直都是手动安装包, 手动安装包可以在配置的时候就排除所有问题包括各种复杂 hook 的定制, 一旦搞定了, 就不动了, 超级稳定, 如果日后有扩展的需求, 因为每个包都是自己配置的, 瞬间就知道怎么来扩展.

Package.el 这种东西对于新手来说是很方便, 但是稍微牛逼一点的包都少不了 mode hook 和自己写一点胶水函数粘贴一下, package.el 安装的是很爽, 各种依赖全部给你搞定, 但是一旦出错了, 鬼知道哪里出错了, 加载出错了? hook 出错了? 和其他包冲突了? 作者手癌犯了, 不小心打错字符了?

这其实就是一个投入产出比的问题, 手动安装包虽然费时, 但是一劳永逸, 相对于 package.el 这种安装时间短, 但是一旦出错调试时间远远超过 package.el 给你节省的安装那点时间, 你会花费更多的时间去分析更复杂的出错原因.

Emacs 本身就是一坨屎墙, 每个人都去墙上抹一点, 你不自己抹, 你最后只会被这些插件之间的复杂性臭死.

分享一下我的Emacs插件折腾流程

  1. 在 github 之间闲逛, 看到作者介绍, 哎哟, 看起来不错哟
  2. magit-submodule-add 加到自己的 lazycat-emacs 目录中
  3. 切换到插件子目录, dired 全选, 直接编译一遍 (这样就不用重启去 load-libraray 了)
  4. 试一下功能, 如果有说找不到某个库, google 看看哪个包, 继续 magit-submodule-add 添加缺少的包, 继续试
  5. 所有包都手动下载好以后, 试验一下, 哎哟, 不错哟, 用 lazy-set-key.el 把这个功能绑定到某个按键上 (默认不启动, 按键的时候临时加载), git commit, 完事
  6. 如果插件不喜欢, 直接 magit-submodule-remove (还没有合并到 magit 代码分支) 移除刚才试验的插件, 避免任何新插件的干扰

这样玩, 大部分插件, 就花最多10分钟就配置好了, 然后就享受这样控制力, 透明度和稳定性, 不会像那些不动脑筋的Emacs用户, 出了错不会排错.

也许很多人说, package.el 多么方便, 我都不会 elisp.

但是我想说的是, 玩 Emacs 这种改装车, 最终你一定是要会 elisp 编程的, 如果你用了那么长时间都不会 elisp 编程, 更不会手动改装 emacs 来满足自己的需要, 为什么你要用 Emacs 呢? 用 VSCode 这些最好, 啥都不用学, 只用就可以了, 作者心情好就帮你更新, 作者没时间, 遇到问题就只能委屈自己了.

我上面说的都是最终达到极致用 Emacs 所需要付出的代价, 如果你不想极致的用, 请忽略我上面的言论.

31 个赞

一般的包(指那些我没修改),直接用 package.el,即便我用 Melpa,我也不会每天更新。此外,Melpa 上的包不见得就是「开发包」,尽管可能这种比较常见,一个包作者完全可以 master 分支用作所谓的「稳定版」。其它有需要修改的包(比如我自己写的和我想要修改的包),我会手动 git clone,然后手动设置,比如:

(use-package swap-regions
  :about "利用 Recursive Edit 交换两个 Region"
  :load-path "~/src/swap-regions.el"
  :ensure t
  :bind ("C-c C-t" . swap-regions))
1 个赞

建议用 submodule, 这样的好处是, 你一旦喜欢某个插件后, 你就可以把这个插件加到自己 emacs 目录中, 而不会占用你的git仓库的空间, 更新的时候, 一旦你本地 git pull 以后, 你的 emacs 只是变动了一个插件的 commit id 而已, 而不是随着插件的更新, 推送一大堆代码到你的 emacs 仓库.

最终, 随着你更新插件的事件推移, 你的 emacs git 仓库依然很小, 方便自己 git clone 下来. 如果不用 submodule , 你自己 emacs 的 commit 大小就等于所有插件的 commit 大小, 以后你自己 clone 自己的 emacs 仓库都会很痛苦.

4 个赞

package.el + quelpa 不够用?

楼主嫌哪里麻烦?不如把痛点说出来,我觉得应该都能解决的。(楼主现在列的痛点我完全 get 不到,能再具体点吗?)

WTF?

???

跟包管理器有关吗?难道不都是一边使用一边开发吗?

如果楼主嫌安装麻烦的话,spacemacs 提供的 :location local 完全不用安装,也挺好用的。

1 个赞

你这不是重新发明了一遍 el-get.el / straight.el 吗?

git submodule 的优点你放弃了,el-get.el / straight.el 的功能又没实现。

@twlz0ne 是有这个问题……我以后可能换到git submodule,el-get/straight对我来说复杂度有点高,有些包装不上有没有解决的头绪。我自己写的代码干了什么我比较清楚,就像我说的,减轻心智负担。

@et2010

package.el的更新要靠interactive的函数(package-list,mark update)自动更新的话要用代码模拟操作,我不是很喜欢。

你一边使用一边开发是怎么操作的?

删除包的时候怎么确保依赖也删除了?尤其是有的时候还有别的包正好依赖那个包?

P.S. 我把两个回复合并了

有时候造轮子就是让自己开心, 不用想那么多, 人生在世, 最重要是自己开心.

就像当年和 rubikitch 一起开发 anything 的时候, thierryvolpiatto 是最后加入进来的, 代码真的写的稀烂.

如果按照大多数人的观点, thierryvolpiatto就不应该自己写 helm, 因为 anything 在十几年前已经非常非常强大了.

后来我和 rubikitch 都因为工作忙停止开发 anyithing , 你看现在的 helm 不是比当年的 anything 还要强大?

所以, 不用想那么多, 开心就好, 别人说啥任他们说吧.

如果你将要安装插件的依赖已经存在你的 emacs 仓库中, 你就不会再装, 已经已经可以 require 了

如果依赖没有被安装, 也用 git-submodule , 或者干脆新建一个文件.

问题的关键就是: 如果新的插件不好用, 你直接用 remove submodule 和 git checkout – . 就可以了啊.

干干净净的, 就像啥都没装一样.

1 个赞

谢谢,这算是听我猫哥讲当年,哈哈 :smile:

原来如此,不过helm这个名字不怎么样,不如前者。现在helm好像也停止开发了。

比较了helm和ivy,最终还是选择helm。

一直都是纯手工管理,放在一个git仓库中,创建一个master分支和testing分支,前者相当于我的稳定分支,后者是我的测试分支。某个包有新版本,就下载下来,合入我的testing分支,用几天没问题之后,merge到我的稳定分支。

试用过package.el,很难管理稳定版本。而且删除包的时候会把没有依赖的包删除,即使是我手动安装的,也会被删。

1 个赞

手动管理有个巨大的好处——确保你的代码是从原作者处获得的。反之带仓库的包管理器都有这方面的安全问题。

从当年的XcodeGhost事件我才感到了这件事情的危险性。假设GNU ELPA或者MELPA的某位管理员因为3年没休假,5年没涨工资,甚至20年都没升职而想搞点破坏的话……

不过我还是喜欢包管理器……

可能是因为我使用的OS一直都有那么个包管理器存在导致我的思维也跟着僵化了吧……

另外还有一个原因是我对所谓的“稳定版”极其反感,我还是喜欢无版本号概念的滚动更新。

1 个赞

你也遇到package-selected-packages的问题了?

自动更新有造好的轮子:

我们谈的可能不是一回事。。。

我也是这么用的,直接使用了use-package的各种特性,需要手动安装的就设置load-path,感觉很方便,但一直担心use-package出问题

手动一样存在风险

我记得几个月前,golang 有个比较基础的包叫做:go-bindata,其作者的 github 账号忽然注销了,很快就有人重新注册了同名账号,再次上架了这个包,然后天下太平,什么事都没发生。

只有少数人知道这事,在推特上讨论,认为假账号存在风险。我当时刚好赶上,一直 fetch 不到,所以也注意到这件事。所以这往后,大家都直接/间接的 fetch 到了这个假账号托管的包,却鲜有人意识到。

不过现在 github 提示 This repository has been archived by the owner. It is now read-only. 了,这个包也改为从其他仓库 fork。可能这个“假账号”的拥有者,并不想搞破坏,只是单纯想把工具链当中丢失的一环衔接上。

除了 package.el 以外的包管理器大多支持本地源。手动管理或许可以降低安全风险,但仍不能避免

手动管理最大的好处,我认为是:解决了网络问题,避免初始化过程中下载失败(我每隔一段时间运行 spacemacs 都会遇到这个问题)

手动管理的缺点:

  1. 管理上给自己增加了负担。
  2. 分享配置的时候,别人对你手动管理的第三方 package 会存在疑虑:你是否为了方便直接修改了源代码(不一定是担心恶意修改),你的版本是多少,或者有人(包括我)就是洁癖,一定要从原始位置下载的文件才肯用。
1 个赞

melpa 上的很多包都不是作者维护的.

比如 melpa 的 tabbar.el 就是bug一大堆.

原来还有很多人从 melpa 来给我的 multi-term.el 的报bug, 我都不知道 melpa 上是谁上传的.

我不反对 melpa , 我只是反感大家把 melpa 当做必需品.

不管手动和还是 melpa 都不能保证是原作者, 因为用户根本就不知道谁创造了这些插件.

我自己个人最反感的就是 melpa 造就了更多不动脑筋的用户, 反而因为自己的水平不行传臭了 emacs 的名声.

4 个赞

最后回复一下: 不管手动管理, 半自动管理, 还是全自动管理, 大家爱用啥就用啥, 自己用的舒服就行了, 都不要讨论了, 有点浪费时间了.

这条发完我也闭嘴了.

自己做饭,去饭馆吃,叫外卖送。

取决于你做菜的水平,及你想成为大厨的意愿。