#+TITLE: makem.sh
#+PROPERTY: LOGGING nil
注意:本自述文件与org-make-toc https://github.com/alphapapa/org-make-toc包配合使用,该包会自动更新目录。
=makem.sh=是一个帮助构建、检查和测试Emacs Lisp包的脚本。它旨在让检查和测试尽可能简单,无需每个包都进行配置。
它的工作方式类似于Makefile,通过调用"规则"来执行诸如字节编译、代码检查、测试等操作。
源文件和测试文件会自动从项目的Git仓库中发现,并自动解析其中的包依赖关系。
输出很简单:默认情况下,除非出现错误,否则不会有输出。随着详细程度的增加,会提供更多积极反馈。默认情况下输出是有颜色的,以方便阅读。
该脚本可以使用开发者本地的Emacs配置运行Emacs,也可以使用一个干净的"沙盒"配置,并可选择在之后删除该配置。当上游依赖可能发布了与开发者个人配置中安装的版本不同的新版本时,这特别有用。
- 目录 :noexport: :PROPERTIES: :TOC: :include siblings :depth 0 :END: :CONTENTS:
- [[#installation][安装]]
- [[#usage][使用]]
- [[#changelog][变更日志]]
- [[#comparisons][比较]] :END:
- 截图 :PROPERTIES: :TOC: :ignore (this) :END:
一些示例输出。第一个显示了以详细程度级别1运行=test=规则的情况,它会显示运行了哪些测试,但除非失败,否则不会显示每个测试的输出:
[[images/make-test-v.png]]
增加详细程度会显示通过测试的绿色输出:
[[images/make-test-vv.png]]
=lint-compile=规则将字节编译器警告视为错误:
[[images/make-lint-compile.png]]
=all=规则运行所有规则并将警告视为错误:
[[images/make-all.png]]
当然,增加详细程度还会显示哪些规则没有发出错误信号:
[[images/make-all-v.png]]
包含的=test.yml= GitHub Actions文件可用于轻松设置CI,输出如下:
[[images/github-action.png]]
- 安装 :PROPERTIES: :TOC: 0 :END:
将=makem.sh=复制到你的包的根目录中。可选地,也可以复制=Makefile=,以便更容易调用脚本。
- 使用 :PROPERTIES: :TOC: :include descendants :END: =makem.sh=脚本可以直接调用或通过=Makefile=调用。使用=makem.sh --help=列出可用的规则。Emacs库=makem.el=提供了一个Transient调度器(类似于Magit),可以轻松地在Emacs中运行带有选定选项的脚本。
:CONTENTS:
- [[#makemsh-script][makem.sh脚本]]
- [[#transient-menu-makemel][Transient菜单(makem.el)]]
- [[#makefile][Makefile]]
- [[#github-action][GitHub Action]]
- [[#github-linguist-statistics][GitHub Linguist统计]]
- [[#git-pre-push-hook][git pre-push钩子]]
- [[#spell-checking][拼写检查]] :END:
** =makem.sh=脚本
可以直接调用脚本来指定其他选项。
#+BEGIN_EXAMPLE makem.sh [选项] 规则...
特定于检查器和测试的规则在找不到相应的检查器或测试时会报错。 使用-vv时,运行多个规则的规则会显示不可用检查器或测试的消息。
规则: all 运行所有检查和测试。 compile 字节编译源文件。
lint 运行所有可用的检查器,忽略不可用的。
lint-checkdoc 运行checkdoc。
lint-compile 字节编译源文件,将警告视为错误。
lint-declare 运行check-declare。
lint-elsa 运行Elsa(不包含在"lint"规则中)。
lint-indent 检查缩进。
lint-package 运行package-lint。
lint-regexps 运行relint。
test, tests 运行所有测试,忽略缺失的测试类型。
test-buttercup 运行Buttercup测试。
test-ert 运行ERT测试。
test-ert-interactive 交互式运行ERT测试。
batch 以批处理模式运行Emacs,自动加载项目源文件和测试文件,
剩余参数(在"--"之后)传递给Emacs。
interactive 交互式运行Emacs,自动加载项目源文件和测试文件,
剩余参数(在"--"之后)传递给Emacs。
选项: -d, --debug 打印调试信息。 -h, --help 我需要帮助! -v, --verbose 增加详细程度,最多-vvv。 --no-color 禁用彩色输出。
--debug-load-path 从Emacs内部打印load-path。
-E, --emacs 路径 在指定路径运行Emacs。
-e, --exclude 文件 从检查和测试中排除指定文件。
-f, --file 文件 除了发现的文件外,还检查指定文件。
-c, --compile-batch 批量编译文件(而不是单独编译;更快,但可能隐藏问题)。
-C, --no-compile 不自动编译文件。
沙盒选项: -s[目录], --sandbox[=目录] 在沙盒目录中用空配置运行Emacs。 如果目录不存在,则创建它。如果未指定目录, 使用临时沙盒目录并在之后删除, 隐含--install-deps和--install-linters。 --install-deps 自动安装包依赖。 --install-linters 自动安装检查器。 -i, --install 包 在运行规则之前安装指定的包。
沙盒内部会自动创建一个Emacs版本特定的子目录,允许使用多个Emacs版本进行测试。
指定沙盒目录时,首次运行使用--install-deps和--install-linters选项,
之后省略这些选项以节省时间。
源文件会自动从git中发现,也可以通过选项指定。包依赖从源文件的 "Package-Requires"头、-pkg.el文件和Cask文件中发现。
Checkdoc的拼写检查器可能无法识别某些单词,导致lint-checkdoc
规则失败。
可以使用变量ispell-buffer-session-localwords
在文件本地或目录本地变量中
添加自定义单词,该变量应设置为字符串列表。
#+END_EXAMPLE
** Transient菜单(=makem.el=)
Elisp文件=makem.el=提供了一个Transient调度器(这个文件应该安装到你的Emacs配置中,而不是项目目录)。使用=M-x makem RET=显示它。
[[images/transient.png]]
** Makefile
提供了一个默认的=Makefile=,它调用=makem.sh=脚本。使用规则名称和可选的详细程度级别调用它,如:
#+BEGIN_SRC sh
运行所有规则
$ make all
运行所有检查
$ make lint
运行所有测试
$ make test
以详细程度级别1运行ERT测试
$ make v=v test-ert
以详细程度级别2运行Buttercup测试
$ make v=vv test-buttercup
在临时沙盒中使用emacs-sandbox.sh运行测试
隐含install-deps=t
$ make sandbox=t test
初始化一个永久沙盒目录DIR(开发者可能选择在必要时手动重新创建它,
否则保留它以节省时间)。然后运行所有检查器和测试。
$ make sandbox=DIR install-deps=t install-linters=t $ make sandbox=DIR all #+END_SRC
** GitHub Action
使用Steve Purcell的[[https://github.com/purcell/setup-emacs][setup-emacs]] Action,可以轻松地为Emacs包在GitHub上设置CI。
- 将=makem.sh=放入你的包的仓库中并使其可执行。
- 将[[file:test.yml][test.yml]](来自=makem.sh=仓库)添加到你的包的仓库中的=.github/workflows/test.yml=。对于大多数Emacs包,它应该可以直接使用,无需修改。
** GitHub Linguist统计
在你的仓库中包含=makem.sh=会影响GitHub由[[Https://github.com/github/linguist][Linguist]]提供的语言统计,这可能导致它被分类为Shell项目而不是Emacs Lisp项目。[[https://github.com/github/linguist#my-repository-is-detected-as-the-wrong-language][Linguist文档]]解释了如何避免这种情况。可能最合适的方法是使用=.gitattributes=文件将=makem.sh=归类为第三方代码,如: #+BEGIN_EXAMPLE sh makem.sh linguist-vendored #+END_EXAMPLE
** git pre-push 钩子
在使用 git 推送之前自动运行测试通常很有帮助。以下是在 =pre-push= 钩子中使用 =makem.sh= 的示例:
#+BEGIN_SRC sh #!/bin/sh
* 提交参数
目前未使用,但作为未来参考很有用。参见 man 5 githooks。
remote="$1" url="$2"
read local_ref local_sha remote_ref remote_sha
* 运行测试
不使用沙盒和自动安装,因为"git push"不应该
导致下载和执行远程代码(即安装包时会发生的情况)。
需要时可以手动完成。然而,在运行于容器中的 CI 系统中,
如果希望在干净的配置下针对最新可用的依赖版本进行测试,
可以使用:
make sandbox=t install-deps=t test
make test #+END_SRC
** 拼写检查
Checkdoc 的拼写检查器可能无法识别某些单词,导致 lint-checkdoc 规则失败。可以使用文件局部或目录局部变量中的 ispell-buffer-session-localwords 变量添加自定义单词,该变量应设置为字符串列表。
- 更新日志 :PROPERTIES: :TOC: :ignore children :END:
** 0.8-pre
变更
- 不必要时不初始化包系统(因为初始化包系统会导致加载其他库,如
url,这可能会掩盖原本可能出现的问题)。([[https://github.com/alphapapa/makem.sh/issues/47][#47]]。感谢 [[https://github.com/josephmturner][Joseph Turner]] 报告。)
修复
- 在
makem.sh的usage函数的 Bash here-document 中转义反引号。(感谢 [[https://github.com/snogge][Ola Nilsson]]。)
兼容性
- 恢复与 4.0 之前的 Bash 版本(如 Mac OS 上)的兼容性。([[https://github.com/alphapapa/makem.sh/pull/49][#49]]。感谢 [[https://github.com/bcc32][Aaron Zeng]]。)
** 0.7.1
修复
- 不使用过时的 Org ELPA 仓库(防止安装过时的 Org 版本)。
** 0.7
新增
- 脚本
makem.sh现在可以从子目录运行(而不必在项目的根目录中)。这也使其可以作为 git 子模块使用。 - 库
makem.el中的命令会自动在项目的目录和子模块中定位 shell 脚本。 - 安装依赖时升级内置包。([[https://github.com/alphapapa/makem.sh/issues/41][#41]]。感谢 [[https://ushin.org/][USHIN]] 赞助此修复。)
修复
- 文件排除正则表达式。([[https://github.com/alphapapa/makem.sh/pull/32][#32]]。感谢 [[https://github.com/fritzgrabo][Fritz Grabo]]。)
致谢
- 感谢 [[https://ushin.org/][ushin]] 贡献子目录/子模块相关的更改。
** 0.6
新增
lint-elint规则(默认未在lint规则中启用,因为 Elint 的输出似乎不太有用)。- =makem.el= 库,带有 Transient 调度器。
- 可以在文件或目录局部变量
ispell-buffer-session-localwords中设置拼写检查的自定义单词。(感谢 [[https://github.com/josephmturner][Joseph Turner]]。) - 允许从项目内的子目录以及 Git 子模块中运行
makem.sh。(感谢 [[https://github.com/josephmturner][Joseph Turner]]。)
修复
- 设置
package-user-dir(Emacs 28 兼容性所需)。 - Emacs 28 的
lint-indent规则。 - 在 CI 中安装 Ispell 用于
checkdoc检查。
内部
- 使用
grep -E代替egrep。([[https://github.com/alphapapa/makem.sh/pull/38][#38]]。感谢 [[https://github.com/jameschensmith][James Chen-Smith]]。)
** 0.5
变更
- 检查时显示所有字节编译警告,而不仅仅是第一个。
** 0.4.2
修复
- 始终将
load-prefer-newer设置为t(而不是仅在初始化包时)。 - 运行
interactive时,除非使用--no-compile,否则自动字节编译源文件,并加载不带扩展名的文件名,以便 Emacs 优先加载字节编译文件。
** 0.4.1
修复
- 显示所有
checkdoc警告,而不仅仅是第一个。
** 0.4
新增
- 详细级别 3(即
-vvv),目前仅用于每个文件的字节编译输出。
修复
- 冗余的字节编译错误消息。
** 0.3
新增
- 选项
-c/--compile-batch以批处理方式在单个 Emacs 进程中编译文件(更快,但可能隐藏问题)。
变更
- 单独编译文件而不是批处理。(较慢,但不会因编译顺序而隐藏问题。)
** 0.2.1
修复
- 使用 =grep= 的 =-a= 参数,以防 Elisp 文件包含控制字符(罕见,但有时必要)。
** 0.2
新增
- 在 =test.yml= 中添加 Emacs 27.1。
** 0.1.1
更新
- =test.yml=:使用新的 GitHub 环境变量语法。(参见[[https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/][通知]],[[https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#environment-files][文档]]。)
** 0.1
首个标记版本。
- 比较 :PROPERTIES: :TOC: ignore-children :END:
有几个类似的工具,每个都略有不同。
注意:
- 在这些比较中,未包括 =makem.sh= 的 Makefile,因为它只提供了一种替代的、=make= 风格的调用约定;它不提供任何功能。
- 这些说明是通过阅读这些项目的文档和源代码编写的,但作者并非这些工具的专家。欢迎更正。
** [[https://github.com/cask/cask][Cask]]
Cask 是一个经典的 Emacs 包项目管理工具。它功能强大且文档完善。它比 =makem.sh= 复杂得多。
- Cask 在使用前需要为每个项目进行配置和初始化。=makem.sh= 设计为无需初始化或配置即可使用。
- Cask 为构建和测试维护一个项目本地的 Emacs 配置。=makem.sh= 提供类似的可选沙盒,以将依赖项与开发者的 Emacs 配置分开安装。
- Cask 旨在通过使用 =curl= 下载脚本并通过管道传输给 Python 来安装。这是一种危险、不安全的反模式,加上代码量大,更加复杂。=makem.sh= 旨在由包开发者复制到位,其代码易于检查。
- Cask 旨在安装在每个开发者的机器上。=makem.sh= 旨在放入包的仓库中,无需本地安装。
- Cask 的文档在[[https://cask.readthedocs.io/en/latest/][其网站]]上详尽且呈现良好。=makem.sh= 可以通过阅读标准的 =--help= 使用指南来使用。
- Cask 包含超过 3,000 行 Emacs Lisp 和 Python 代码。=makem.sh= 大约有 600 行非常简单的单文件代码。
** [[https://github.com/doublep/eldev][Eldev]]
Eldev 是一个功能强大、灵活的工具。它有许多功能,可以针对每个项目进行扩展和配置。它的设计比 =makem.sh= 复杂得多。
- Eldev 在使用前需要对每个项目进行一些初始化和配置。=makem.sh= 设计为无需初始化或配置即可使用。
- Eldev 将依赖项安装在软件包仓库中特定 Emacs 版本的目录中,这允许使用多个 Emacs 版本进行测试。=makem.sh= 可以使用开发者本地 Emacs 配置中已有的依赖项,或者使用内置的沙盒来单独安装依赖项;它不支持单独的、特定 Emacs 版本的沙盒。
- Eldev 的安装方式是使用 =curl= 下载脚本并通过管道传输到 shell。这是一种危险且不安全的反模式,加上代码量大更是如此。=makem.sh= 旨在由软件包开发者直接复制到位,其代码易于检查。
- Eldev 需要在每个开发者的机器上本地安装。=makem.sh= 旨在直接放入软件包的仓库中,无需本地安装。
- Eldev 的文档全面且编写良好,约有 8,000 字。=makem.sh= 可以通过阅读标准的 =--help= 使用指南来使用。
- Eldev 在 Emacs 内部运行,与正在运行的操作(如测试)在同一进程中。=makem.sh= 在 Emacs 外部运行,每个操作都在单独的 Emacs 进程中执行。
- Eldev 有超过 4,000 行密集代码,分布在 8 个源文件中。=makem.sh= 约有 600 行非常简单的代码,集中在一个文件中。
** [[https://github.com/vermiculus/emake.el][emake]]
=emake= 用于持续集成测试。它功能强大、文档完善,并提供了比 =makem.sh= 更具体的灵活性。
- =emake= 要求在使用前配置多个项目特定和 Emacs 特定的变量。=makem.sh= 设计为无需初始化或配置即可使用。
- 看起来 =emake= 可以在本地运行,而不仅仅在 Travis CI 或 GitHub Actions 等远程系统上运行,但需要进行大量配置和初始化。=makem.sh= 设计为在本地开发者系统和远程 CI 测试中使用同样简单。
- =emake= 提供了一些工具,用于在 CI 系统上构建特定的 Emacs 版本。=makem.sh= 本身只使用本地安装的 Emacs 版本;对于 CI 使用,提供了一个 GitHub Actions 配置,使用其他工具安装特定的 Emacs 版本。
- =emake= 的安装方式是使用 =curl= 下载脚本并通过管道传输到 shell,而且似乎在运行时还会进一步下载远程 shell 脚本,至少在初始化时是这样。这是一种危险且不安全的反模式。=makem.sh= 旨在由软件包开发者直接复制到位,其代码易于检查。除了根据要求安装 Emacs 软件包依赖项外,不会下载任何远程代码。
- =emake= 的文档全面且编写良好,约有 2,000 字。=makem.sh= 可以通过阅读标准的 =--help= 使用指南来使用。
- =emake= 是一个 700 行的 Emacs Lisp 文件,还有一个可选的 100 行 Makefile 提供一些默认配置。=makem.sh= 约有 600 行 Bash 代码,集中在一个文件中。
** [[https://gitlab.petton.fr/DamienCassou/makel/][makel]]
在这些替代方案中,=makel= 最像 =makem.sh=。它简单且几乎不需要配置。
- =makel= 在使用前需要配置几个变量。=makem.sh= 设计为无需初始化或配置即可使用。
- =makel= 可以安装手动指定的软件包依赖项,并且似乎将它们下载到本地软件包仓库目录中。=makem.sh= 只将依赖项安装到沙盒目录中,默认情况下,这是一个自动删除的临时目录。
- =makel= 可以在远程 CI 系统上使用,但没有提供特定的集成工具。=makem.sh= 提供了一个可以直接使用的 GitHub Actions 文件。
- =makel= 的使用方式是将两个 Make 文件复制到项目仓库目录中。它建议在其中一个文件不存在时允许自动从互联网下载。=makem.sh= 旨在由软件包开发者直接复制到位。除了根据要求安装 Emacs 软件包依赖项外,不会下载任何远程代码。
- =makel= 没有内置文档,但使用非常简单。=makem.sh= 可以通过阅读标准的 =--help= 使用指南来使用。
- =makel= 是一个文件中约 150 行的 Make 代码。=makem.sh= 约有 600 行 Bash 代码,集中在一个文件中。
- 鸣谢
灵感来自 Damien Cassou 的优秀项目 [[https://gitlab.petton.fr/DamienCassou/makel][makel]]。
- 开发
Bug 报告、功能请求、建议 — 欢迎提出!
- 许可证
GPLv3