=makem=.sh is a script that helps to build, lint, and test Emacs Lisp packages. It aims to make linting and testing as simple as possible without requiring per-package configuration.
It works similarly to a Makefile in that "rules" are called to perform actions such as byte-compiling, linting, testing, etc.
Source and test files are discovered automatically from the project's Git repo, and package dependencies within them are parsed automatically.
Output is simple: by default, there is no output unless errors occur. With increasing verbosity levels, more detail gives positive feedback. Output is colored by default to make reading easy.
The script can run Emacs with the developer's local Emacs configuration, or with a clean, "sandbox" configuration that can be optionally removed afterward. This is especially helpful when upstream dependencies may have released new versions that differ from those installed in the developer's personal configuration.
Contents :noexport: :PROPERTIES: :TOC: :include siblings :depth 0 :END: :CONTENTS:
[[#installation][Installation]]
[[#usage][Usage]]
[[#changelog][Changelog]]
[[#comparisons][Comparisons]] :END:
Screenshots :PROPERTIES: :TOC: :ignore (this) :END:
Some example output. The first shows running the =test= rule with verbosity level 1, which shows which tests are run but omits each test's output unless it fails:
[[images/make-test-v.png]]
Increasing the verbosity shows green output for passing tests:
[[images/make-test-vv.png]]
The =lint-compile= rule treats byte-compiler warnings as errors:
[[images/make-lint-compile.png]]
The =all= rule runs all rules and treats warnings as errors:
[[images/make-all.png]]
Of course, with increased verbosity, it also shows which rules did not signal errors:
[[images/make-all-v.png]]
The included =test.yml= GitHub Actions file can be used to easily set up CI, giving output like:
[[images/github-action.png]]
Copy =makem.sh= into your package's root directory. Optionally, also copy =Makefile=, to make calling the script easier.
:CONTENTS:
** =makem.sh= script
The script may be called directly to specify additional options.
makem.sh [OPTIONS] RULES...
Linter- and test-specific rules will error when their linters or tests are not found. With -vv, rules that run multiple rules will show a message for unavailable linters or tests.
Rules: all Run all lints and tests. compile Byte-compile source files.
lint Run all linters, ignoring unavailable ones.
lint-checkdoc Run checkdoc.
lint-compile Byte-compile source files with warnings as errors.
lint-declare Run check-declare.
lint-elsa Run Elsa (not included in "lint" rule).
lint-indent Lint indentation.
lint-package Run package-lint.
lint-regexps Run relint.
test, tests Run all tests, ignoring missing test types.
test-buttercup Run Buttercup tests.
test-ert Run ERT tests.
test-ert-interactive Run ERT tests interactively.
batch Run Emacs in batch mode, loading project source and test files
automatically, with remaining args (after "--") passed to Emacs.
interactive Run Emacs interactively, loading project source and test files
automatically, with remaining args (after "--") passed to Emacs.
Options: -d, --debug Print debug info. -h, --help I need somebody! -v, --verbose Increase verbosity, up to -vvv. --no-color Disable color output.
--debug-load-path Print load-path from inside Emacs.
-E, --emacs PATH Run Emacs at PATH.
-e, --exclude FILE Exclude FILE from linting and testing.
-f, --file FILE Check FILE in addition to discovered files.
-c, --compile-batch Batch-compile files (instead of separately; quicker, but
may hide problems).
-C, --no-compile Don't compile files automatically.
Sandbox options: -s[DIR], --sandbox[=DIR] Run Emacs with an empty config in a sandbox DIR. If DIR does not exist, make it. If DIR is not specified, use a temporary sandbox directory and delete it afterward, implying --install-deps and --install-linters. --install-deps Automatically install package dependencies. --install-linters Automatically install linters. -i, --install PACKAGE Install PACKAGE before running rules.
An Emacs version-specific subdirectory is automatically made inside
the sandbox, allowing testing with multiple Emacs versions. When
specifying a sandbox directory, use options --install-deps and
--install-linters on first-run and omit them afterward to save time.
Source files are automatically discovered from git, or may be specified with options. Package dependencies are discovered from "Package-Requires" headers in source files, from -pkg.el files, and from a Cask file.
Checkdoc's spell checker may not recognize some words, causing the
lint-checkdoc' rule to fail. Custom words can be added in file-local or directory-local variables using the variable
ispell-buffer-session-localwords', which should be set to a list of
strings.
** Transient menu (=makem.el=)
The Elisp file =makem.el= provides a Transient dispatcher (this file should be installed into your Emacs configuration rather than into a project's directory). Use =M-x makem RET= to show it.
[[images/transient.png]]
** Makefile
A default =Makefile= is provided which calls the =makem.sh= script. Call it with the name of a rule and an optional verbosity level, like:
$ make all
$ make lint
$ make test
$ make v=v test-ert
$ make v=vv test-buttercup
$ make sandbox=t test
$ make sandbox=DIR install-deps=t install-linters=t $ make sandbox=DIR all
** GitHub Action
Using Steve Purcell's [[https://github.com/purcell/setup-emacs][setup-emacs]] Action, it's easy to set up CI on GitHub for an Emacs package.
** GitHub Linguist statistics
Having =makem.sh= in your repository will affect GitHub's language stats provided by [[Https://github.com/github/linguist][Linguist]], which might cause it to be classified as a Shell project rather than an Emacs Lisp one. The [[https://github.com/github/linguist#my-repository-is-detected-as-the-wrong-language][Linguist documentation]] explains how to avoid this. Probably the most appropriate way is to use a =.gitattributes= file to classify =makem.sh= as vendored, like:
makem.sh linguist-vendored
** git pre-push hook
It's often helpful to run tests automatically before pushing with git. Here's an example of using =makem.sh= in a =pre-push= hook:
remote="$1" url="$2"
read local_ref local_sha remote_ref remote_sha
make test
** Spell checking
Checkdoc's spell checker may not recognize some words, causing the ~lint-checkdoc~ rule to fail. Custom words can be added in file-local or directory-local variables using the variable ~ispell-buffer-session-localwords~, which should be set to a list of strings.
** 0.8-pre
Changes
Fixes
Compatibility
** 0.7.1
Fixes
** 0.7
Added
Fixed
Credits
** 0.6
Added
Fixed
Internal
** 0.5
Changed
** 0.4.2
Fixed
** 0.4.1
Fixed
** 0.4
Added
Fixed
** 0.3
Added
Changed
** 0.2.1
Fixed
** 0.2
Added
** 0.1.1
Updated
** 0.1
First tagged release.
There are several similar tools, each of which is slightly different.
Notes:
** [[https://github.com/cask/cask][Cask]]
Cask is a classic Emacs package project management tool. It's powerful and well-documented. It's much more sophisticated than =makem.sh=.
** [[https://github.com/doublep/eldev][Eldev]]
Eldev is a powerful, flexible tool. It has many features and can be extended and configured for each project. It's designed to be much more sophisticated than =makem.sh=.
** [[https://github.com/vermiculus/emake.el][emake]]
=emake= is intended for continuous integration testing. It is powerful and well-documented, and provides some more specific flexibility than =makem.sh=.
** [[https://gitlab.petton.fr/DamienCassou/makel/][makel]]
Of these alternatives, =makel= is most like =makem.sh=. It's simple and requires little configuration.
=makel= requires configuring several variables before use. =makem.sh= is designed to work without initialization or configuration.
=makel= can install package dependencies which are manually specified, and it appears to download them into the local package repo directory. =makem.sh= only installs dependencies into a sandbox directory, which, by default, is a temporary directory that is automatically removed.
=makel= can be used on remote CI systems, but no specific integration tools are provided. =makem.sh= provides a GitHub Actions file that can be used as-is.
=makel= is intended to be used by copying two Make files into the project repo directory. It recommends allowing one of them to download the other automatically from the Internet when not present. =makem.sh= is intended to be copied into place by the package developer. No remote code is downloaded, other than installing Emacs package dependencies when requested.
=makel= provides no built-in documentation, but it is very simple to use. =makem.sh= can be used by reading a standard =--help= usage guide.
=makel= is about 150 lines of Make code in one file. =makem.sh= is about 600 lines of Bash code in one file.
Credits :PROPERTIES: :TOC: :ignore this :END:
Inspired by Damien Cassou's excellent [[https://gitlab.petton.fr/DamienCassou/makel][makel]] project.
Bug reports, feature requests, suggestions — /oh my/!
GPLv3