All Projects → doublep → Eldev

doublep / Eldev

Licence: gpl-3.0
Elisp Development Tool

Projects that are alternatives of or similar to Eldev

Jacoco Plugin
Jenkins JaCoCo Plugin
Stars: ✭ 119 (+33.71%)
Mutual labels:  developer-tools, continuous-integration
Instapack
All-in-one TypeScript and Sass compiler for web applications! 📦 🚀
Stars: ✭ 131 (+47.19%)
Mutual labels:  build-tool, developer-tools
Fabric8
fabric8 is an open source microservices platform based on Docker, Kubernetes and Jenkins
Stars: ✭ 1,783 (+1903.37%)
Mutual labels:  developer-tools, continuous-integration
Fabric8 Platform
Generates the distribution of the fabric8 microservices platform
Stars: ✭ 105 (+17.98%)
Mutual labels:  developer-tools, continuous-integration
Bake
A build system that lets you clone, build and run C/C++ projects with a single command
Stars: ✭ 434 (+387.64%)
Mutual labels:  build-tool, developer-tools
Cml
♾️ CML - Continuous Machine Learning | CI/CD for ML
Stars: ✭ 2,843 (+3094.38%)
Mutual labels:  developer-tools, continuous-integration
Awesome Ci
List of Continuous Integration services
Stars: ✭ 2,737 (+2975.28%)
Mutual labels:  developer-tools, continuous-integration
alfred
(v0.2) Even Batman needs a little help. Task runner. Automator. Build system.
Stars: ✭ 62 (-30.34%)
Mutual labels:  developer-tools, build-tool
Circleci Cli
Use CircleCI from the command line
Stars: ✭ 297 (+233.71%)
Mutual labels:  developer-tools, continuous-integration
Cake
🍰 Cake (C# Make) is a cross platform build automation system.
Stars: ✭ 3,154 (+3443.82%)
Mutual labels:  build-tool, continuous-integration
Toast
Containerize your development and continuous integration environments. 🥂
Stars: ✭ 748 (+740.45%)
Mutual labels:  build-tool, continuous-integration
Flubucore
A cross platform build and deployment automation system for building projects and executing deployment scripts using C# code.
Stars: ✭ 695 (+680.9%)
Mutual labels:  build-tool, continuous-integration
Localstack
💻 A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!
Stars: ✭ 37,724 (+42286.52%)
Mutual labels:  continuous-integration, developer-tools
Npm Scripts Info
Display the description of your npm scripts
Stars: ✭ 85 (-4.49%)
Mutual labels:  build-tool
Postinstall Build
Helper for conditionally building your npm package on postinstall
Stars: ✭ 87 (-2.25%)
Mutual labels:  build-tool
Cacher Cli
The command line interface to Cacher.
Stars: ✭ 85 (-4.49%)
Mutual labels:  developer-tools
Dbuild
Multi-project build tool, based on sbt.
Stars: ✭ 84 (-5.62%)
Mutual labels:  build-tool
Suitcase
A flexible command line tool for instantly deploying user interfaces for simple commands and scripts.
Stars: ✭ 1,287 (+1346.07%)
Mutual labels:  developer-tools
Ptags
A parallel universal-ctags wrapper for git repository
Stars: ✭ 87 (-2.25%)
Mutual labels:  developer-tools
Dumb Jump
an Emacs "jump to definition" package for 50+ languages
Stars: ✭ 1,256 (+1311.24%)
Mutual labels:  developer-tools

:toc: macro :toc-title: Table of contents :source-language: lisp ifndef::env-github[:icons: font] ifdef::env-github[] :warning-caption: ⚠️ :caution-caption: 🔥 :important-caption: ❗️ :note-caption: 📎 :tip-caption: 💡 endif::[] :uri-search-github: https://github.com/search?q=filename%3AEldev+path%3A%2F :uri-flycheck: https://www.flycheck.org/ :uri-flycheck-eldev: https://github.com/flycheck/flycheck-eldev :uri-ert: https://www.gnu.org/software/emacs/manual/html_node/ert/index.html :uri-ert-sel: https://www.gnu.org/software/emacs/manual/html_node/ert/Test-Selectors.html :uri-buttercup: https://github.com/jorgenschaefer/emacs-buttercup :uri-buttercup-rt: https://github.com/jorgenschaefer/emacs-buttercup/blob/master/docs/running-tests.md :uri-cask: https://github.com/cask/cask :uri-makem-sh: https://github.com/alphapapa/makem.sh :uri-emake: https://github.com/vermiculus/emake.el :uri-keg: https://github.com/conao3/keg.el :uri-makel: https://gitea.petton.fr/DamienCassou/makel :uri-evm: https://github.com/rejeep/evm :uri-nix-emacs: https://github.com/purcell/nix-emacs-ci :uri-package-lint: https://github.com/purcell/package-lint :uri-relint: https://github.com/mattiase/relint :uri-elisp-lint: https://github.com/gonewest818/elisp-lint/ :uri-projectile: https://github.com/bbatsov/projectile :uri-github-wflows: https://help.github.com/en/actions :uri-travis: https://travis-ci.org/ :uri-circle: https://circleci.com/ :uri-docker: https://www.docker.com/ :uri-coveralls: https://coveralls.io/ :uri-setup-emacs: https://github.com/purcell/setup-emacs :uri-undercover: https://github.com/undercover-el/undercover.el :uri-extmap: https://github.com/doublep/extmap :uri-iter2: https://github.com/doublep/iter2 :uri-logview: https://github.com/doublep/logview :uri-datetime: https://github.com/doublep/datetime :since-0-1-1: image:https://img.shields.io/badge/since-0.1.1-8be[Since 0.1.1,float=right] :since-0-2: image:https://img.shields.io/badge/since-0.2-8be[Since 0.2,float=right] :since-0-2-1: image:https://img.shields.io/badge/since-0.2.1-8be[Since 0.2.1,float=right] :since-0-3: image:https://img.shields.io/badge/since-0.3-8be[Since 0.3,float=right] :since-0-3-2: image:https://img.shields.io/badge/since-0.3.2-8be[Since 0.3.2,float=right] :since-0-4: image:https://img.shields.io/badge/since-0.4-8be[Since 0.4,float=right] :since-0-5: image:https://img.shields.io/badge/since-0.5-8be[Since 0.5,float=right] :since-0-6: image:https://img.shields.io/badge/since-0.6-8be[Since 0.6,float=right] :since-0-7: image:https://img.shields.io/badge/since-0.7-8be[Since 0.7,float=right] :since-0-8: image:https://img.shields.io/badge/since-0.8-8be[Since 0.8,float=right]

= Eldev

image:https://img.shields.io/badge/license-GPL_3-green.svg[License: GPL 3, link=http://www.gnu.org/licenses/gpl-3.0.txt] image:https://img.shields.io/github/release/doublep/eldev.svg[Latest release, link=https://github.com/doublep/eldev/releases] image:http://stable.melpa.org/packages/eldev-badge.svg[MELPA Stable, link=http://stable.melpa.org/#/eldev] image:https://github.com/doublep/eldev/workflows/CI/badge.svg[CI, link=https://github.com/doublep/eldev/actions?query=workflow%3ACI]

Eldev (Elisp Development Tool) is an Emacs-based build tool, targeted solely at Elisp projects. It is an alternative to Cask. Unlike Cask, Eldev itself is fully written in Elisp and its configuration files are also Elisp programs. If you are familiar with Java world, Cask can be seen as a parallel to Maven — it uses project description, while Eldev is sort of a parallel to Gradle — its configuration is a program on its own.

toc::[]

== Brief overview

Eldev features:

  • Eldev configuration is Elisp. It can change many defaults or even define additional commands and options.
  • Built-in support for regression/unit testing.
  • There are four levels of configuration — you can customize most aspects of Eldev for your project needs and personal preferences.
  • You can use local dependencies, even those that don’t use Eldev (though some restrictions do apply, of course). This is similar to Cask linking, but with more flexibility.
  • Eldev is fast.

Drawbacks:

  • Eldev doesn’t run the project being tested/built in a separate process, so it is not as pure as Cask. However, Emacs packages won’t live in a sterile world anyway: typical user setup will include dozens of other packages.
  • Eldev depends much more on Emacs internals. It is more likely to break with future Emacs versions than Cask.
  • Eldev is a recent development and is {uri-search-github}[not widely used], so there can be bugs. However, Eldev contains a reasonably large regression test collection, so it is not completely untested.

TIP: If you are using {uri-flycheck}[Flycheck], check out {uri-flycheck-eldev}[flycheck-eldev] package. It provides integration between Flycheck and Eldev, allowing the former to automatically use proper dependencies in Eldev projects.

=== Example projects

Here is a non-exhaustive list of projects that use Eldev and can serve as examples. I intentionally list only my own projects, even if there are others, because this way it’s easier to ensure that the comments below stay valid. Eldev source code itself comes with no examples: I think real-world usage provides better models.

{uri-extmap}[extmap]; its file {uri-extmap}/blob/master/Eldev[Eldev]; its file {uri-extmap}/blob/master/.github/workflows/test.yml[.github/workflows/test.yml]::

A simple project with no dependencies.  As you can see, there is
nothing in its `Eldev`.  The file is actually not even needed, it
is only there to signify that Eldev can be used on the project and
for some tools ({uri-flycheck-eldev}[flycheck-eldev],
{uri-projectile}[Projectile]).

{uri-iter2}[iter2]; its file {uri-iter2}/blob/master/Eldev[Eldev]; its file {uri-iter2}/blob/master/.travis.yml[.travis.yml]::

Another simple project with no dependencies.  However, it uses its
file `Eldev` to define a custom option that activates
project-specific development assistance code.  Additionally, it
enables <<undercover-plugin,undercover>> plugin to collect test
code coverage statistics.

{uri-logview}[Logview]; its file {uri-logview}/blob/master/Eldev[Eldev]; its file {uri-logview}/blob/master/.github/workflows/test.yml[.github/workflows/test.yml]::

This project has several dependencies, so it needs to instruct
Eldev how to find them.  Additionally, it deactivates (i.e. makes
it do nothing even if specified) one option provided by Eldev,
because of what seems like a bug in Emacs 24.

{uri-datetime}[datetime]; its file {uri-datetime}/blob/master/Eldev[Eldev]; its file {uri-datetime}/blob/master/.github/workflows/test.yml[.github/workflows/test.yml]::

A library with a fairly complicated file `Eldev`.  The main reason
for complexity are two included Java programs that are used for 1)
extracting information from Java core libraries; and 2) comparing
``datetime``’s results against a Java implementation during
testing.  It also uses `extmap` to generate resource files that
are later included in its package.

All these projects also use <<continuous-integration,continuous integration>> on either <<github-workflows,GitHub>> or <<travis-ci,Travis CI>> for automated testing. Various elements of files Eldev in these projects are documented below.

== Requirements

Eldev runs on Emacs 24.4 and up. On earlier Emacs versions it will be overly verbose, but this is rather an Emacs problem.

Linux, macOS or other POSIX-like system is currently required. However, since there is only a small shell script that is really OS-dependent, porting to other systems should not be difficult (volunteers welcome).

Eldev intentionally has no dependencies, at least currently: otherwise your project would also see them, which could in theory lead to some problems.

== Installation

There are several ways to install Eldev.

[discrete] ==== Bootstrapping from MELPA: if you have a catch-all directory for executables

. From this directory (e.g. ~/bin) execute: +

$ curl -fsSL https://raw.github.com/doublep/eldev/master/bin/eldev > eldev && chmod a+x eldev

You can even do this from /usr/local/bin provided you have the necessary permissions.

No further steps necessary — Eldev will bootstrap itself as needed on first invocation.

[discrete] ==== Bootstrapping from MELPA: general case

. Execute: +

$ curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/eldev | sh

This will install eldev script to ~/.eldev/bin.

. Add the directory to your $PATH; e.g. in ~/.profile add this: + export PATH="$HOME/.eldev/bin:$PATH"

Afterwards Eldev will bootstrap itself as needed on first invocation.

TIP: eldev doesn’t really need to be findable through $PATH — it will work regardless. This is rather for your convenience, so that you don’t need to type the full path again and again.

[discrete] ==== Installing from sources

. Clone the source tree from GitHub.

. In the cloned working directory execute: +

$ ./install.sh DIRECTORY

Here DIRECTORY is the location of eldev executable should be put. It should be in $PATH environment variable, or else you will need to specify full path each time you invoke Eldev. You probably have sth. like ~/bin in your $PATH already, which would be a good value for DIRECTORY. You could even install in e.g. /usr/local/bin — but make sure you have permissions first.

[discrete] ==== Mostly for developing Eldev itself

. Clone the source tree from GitHub.

. Set environment variable $ELDEV_LOCAL to the full path of the working directory.

. Make sure executable eldev is available. Either follow any of the first way to install Eldev, or symlink/copy file bin/eldev from the cloned directory to somewhere on your $PATH.

Now each time Eldev is executed, it will use the sources at $ELDEV_LOCAL. You can even modify it and see how that affects Eldev immediately.

=== Upgrading Eldev

Eldev bootstraps itself when needed, but won’t automatically fetch new versions. To upgrade it later, explicitly run (from any directory):

$ eldev upgrade-self

By default it uses MELPA Stable. If you want to test or use some not yet officially released version, try:

$ eldev --unstable upgrade-self

This will make it use MELPA Unstable for upgrading. If you want to switch back to the latest stable version (as recommended), supply -d (--downgrade) option to the command:

$ eldev upgrade-self -d

== Safety concerns

TIP: In general, it is not recommended to execute Eldev, GNU Make, Scons, any other build tool or anything based on one in a directory that contains untrusted code.

Like many (if not most) other development tools, Eldev is unsafe when executed on untrusted code. For example, simply running eldev in a project you have just downloaded from hackerden.org can result in anything, including emptied home directory. For that matter, running make or gradle is not better in this regard. Eldev is perhaps a bit more dangerous, because even eldev help reads file Eldev, thus executing arbitrary code.

Even seemingly harmless things, like opening a .el file in Emacs can lead to unforeseen consequences. If you e.g. have {uri-flycheck}[Flycheck] enabled everywhere, this will result in byte-compiling said file, which also can execute arbitrary code, for example using (eval-when-compile ...) form. The same holds for installing (not even using!) Elisp packages.

Only use build tools on code that you trust. Better yet, don’t even touch code that you don’t plan running.

== Getting started

Eldev comes with built-in help. Just run:

$ eldev help

This will list all the commands Eldev supports. To see detailed description of any of those, type:

$ eldev help COMMAND

In the help you can also see lots of options — both global and specific to certain commands. Many common things are possible just out of the box, but <<extending-eldev,later we will discuss>> how to define additional commands and options or change defaults for the existing.

Two most important global options to remember are --trace (-t) and --debug (-d). With the first one, Eldev prints lots of additional information about what it is doing to stdout. With the second, Eldev prints stacktraces for most errors. These options will often help you figure out what’s going wrong without requesting any external assistance. Also check out section on <<debugging-features,various debugging features>> discussed later.

Eldev mostly follows GNU conventions in its command line. Perhaps the only exception is that global options must be specified before command name and command-specific options — after it.

== Initializing a project

When Eldev starts up, it configures itself for the project in the directory where it is run from. This is done by loading Elisp file called Eldev (without extension!) in the current directory. This file is similar to Make’s Makefile or Cask’s Cask. But even more so to Gradle’s build.gradle: because it is a program. File Eldev is not strictly required, but nearly all projects will have one. It is also generally recommended to create it even if empty, because otherwise some tools (e.g. {uri-flycheck-eldev}[flycheck-eldev], {uri-projectile}[Projectile]) will not recognize the project as Eldev-based without it.

You can create the file in your project manually, but it is easier to just let Eldev itself do it for you, especially the first time:

$ eldev init

If you let the initializer do its work, it will create file Eldev already prepared to download project dependencies. If you answer “no” to its question (or execute as eldev init --non-interactive), just edit the created file and uncomment some of the calls to eldev-use-package-archive there as appropriate. These forms instruct Eldev to use specific package archives to download project dependencies.

After this step, Eldev is ready to work with your project.

[#setup-procedure] == Setup procedure in details

Now that we have created file Eldev, it makes sense to go over the full startup process:

  • Load file ~/.eldev/config
  • Load file Eldev in the current directory
  • Load file Eldev-local in the current directory
  • Execute setup forms specified on the command line

None of these Elisp files and forms are required. They are also not restricted in what they do. However, their intended usage is different.

File ~/.eldev/config is user-specific. It is meant mostly for customizing Eldev to your personal preferences. For example, if you hate coloring of Eldev output, add form (setf eldev-coloring-mode nil) to it. Then every Eldev process started for any project will default to using uncolored output.

File Eldev is project-specific. It is the only configuration file that should be added to project’s VCS (Git, Mercurial, etc.). Typical usage of this file is to define in which package archives to look up dependencies. It is also the place to define project-specific builders and commands, for example to build project documentation from source.

File Eldev-local is working directory or user/project-specific. Unlike Eldev, it should not be added to VCS: it is meant to be created by each developer (should he want to do so) to customize how Eldev behaves in this specific directory. The most common use is to define local dependencies. A good practice is to instruct your VSC to ignore this file, e.g. list it in .gitignore for Git.

Finally, it is possible to specify some (short) setup forms on the command line using --setup (-S) option. This is not supposed to be used often, mostly in cases where you run Eldev on a use-once project checkout, e.g. on a <<continuous-integration,continuous integration>> server.

[#project-isolation] === Project isolation

Eldev tries to create a self-contained environment for building and testing your project. It will isolate your project as much as possible from your “normal” Emacs, i.e. the one that you use for editing. This is done to avoid interference from your other installed packages or configuration, to prevent broken and misbehaving projects from affecting your Emacs and, finally, to simplify testing of certain “permanent effect” features, like customizing variables.

  • Packages installed in your Emacs (usually in ~/.emacs.d/elpa/) are not visible for projects built with Eldev. Likewise, dependencies installed for such projects will not appear in your normal Emacs.

  • {since-0-2-1} Variable user-emacs-directory will point somewhere inside .eldev in the project’s directory rather than to ~/.emacs.d. This also means that locate-user-emacs-file will not find files in your normal configuration directory. If you want to undo this change (e.g. in file Eldev or Eldev-local), use original value of the variable stored as eldev-real-user-emacs-directory.

  • Eldev supports <<different-emacs-versions,executing on different Emacs version>> for the same project without any additional steps.

=== Using preinstalled dependencies

{since-0-8} Starting with version 0.8 you can opt out of some of the default project isolation features and use preinstalled dependencies, e.g. those from your normal Emacs. To activate this mode, use global option --external (-X), e.g.:

$ eldev -X test

In this mode Eldev will expect dependencies to be installed in given directory (standard Emacs location — ~/.emacs.d/elpa — is only the default: you can use another directory). If a dependency is not installed, Eldev will not install it on its own: it doesn’t know which package archives should be used. Likewise, it will not upgrade anything. In all such cases, i.e. when required dependencies are not correctly preinstalled in the specified external directory, Eldev will simply fail.

<<local-dependencies,Local dependencies>> discussed later take precedence even in this mode: anything declared as local will override dependencies available from an external directory, just like it will in usual full isolation mode.

This mode can be useful to load exactly the same dependency versions as those installed in your normal Emacs. However, it is not suitable for continuous integration or for working on packages that you do not have — for whatever reason — installed normally. It is also difficult to test on <<different-emacs-versions,different Emacs versions>> in external directory mode. Therefore, it is not the default. But, as usual in Eldev, you can make it the default in file ~/.eldev/config if you want.

== Project dependencies

Eldev picks up project dependencies from package declaration, i.e. usually from Package-Requires header in the project’s main .el file. If you have several files with package headers in the the root directory, you need to set variable eldev-project-main-file, else function package-dir-info can pick a wrong one. In any case, you don’t need to declare these dependencies second time in Eldev and keep track that they remain in sync.

However, you do need to tell Eldev how to find these dependencies. Like Cask, by default it doesn’t use any package archives. To tell it to use an archive, call function eldev-use-package-archive in Eldev (you have such forms already in place if you have used eldev init). For example:

[source]

(eldev-use-package-archive 'melpa)

Eldev knows about two “standard” archives, which should cover most of your needs: gnu and melpa. When using MELPA, you can also explicitly choose melpa-stable or melpa-unstable instead.

{since-0-5} A better way is provided by two global options: --stable (the default) and --unstable. Normally, Eldev will try to install everything from MELPA Stable (you wouldn’t want your tests fail only because a dependency in an unstable version has a bug). However, if a package is not available (at all or in the required version) from the stable archive, unstable will be used automatically. If you specify --unstable on the command line, Eldev will behave in the opposite way: prefer the unstable archive and use the stable only as a fallback.

Emacs 25 and up supports package archive priorities. Eldev backports this to Emacs 24 and utilizes the feature to assign the standard archives it knows about priorities 300 (for GNU ELPA), 200 and 100 (for MELPA Stable/Unstable). A dependency from a package with a lower priority is installed only if there are no other options.

If dependencies for your project are only available from some other archive, you can still use the same function. Just substite the symbolic archive name with a cons cell of name and URL as strings:

[source]

(eldev-use-package-archive '("myarchive" . "http://my.archive.com/packages/"))

You don’t need to perform any additional steps to have Eldev actually install the dependencies: any command that needs them will make sure they are installed first. However, if you want to check if package archives have been specified correctly and all dependencies can be looked up without problems, you can explicitly use command prepare.

[#local-dependencies] === Local dependencies

Imagine you are developing more than one project at once and they depend on each other. You’d typically want to test the changes you make in one of them from another right away. If you are familiar with Cask, this is solved by linking projects in it.

Eldev provides a more flexible approach to this problem called local dependencies. Let’s assume you develop project foo in directory ~/foo and also a library called barlib in ~/barlib. And foo uses the library. To have Eldev use your local copy of barlib instead of downloading it e.g. from MELPA, add the following form in file ~/foo/Eldev-local:

[source]

(eldev-use-local-dependency "~/barlib")

Note that the form must not be added to Eldev: other developers who check out your project probably don’t even have a local copy of barlib or maybe have it in some other place. In other words, this should really remain your own private setting and go to Eldev-local.

Local dependencies have loading modes, just as the project’s package itself. Those will be discussed <<loading-modes,later>>.

Eldev correctly handles situations with changing definitions of local dependencies. I.e. by simply commenting out or uncommenting eldev-use-local-dependency call, you can quickly test your project both with a MELPA-provided package and with a local dependency — Eldev will adapt without any additional work from you.

[#additional-dependencies] === Additional dependencies

It is possible to register additional dependencies for use only by certain Eldev commands. Perhaps the most useful is to make certain packages available for testing purposes. For example, if your project doesn’t depend on package foo on its own, but your test files do, add the following form to Eldev file:

[source]

(eldev-add-extra-dependencies 'test 'foo)

Additional dependencies are looked up in the same way as normal ones. So, you need to make sure that all of them are available from the package archives you instructed Eldev to use.

The following commands make use of additional dependencies: build, emacs, eval, exec and test. Commands you define yourself can also take advantage of this mechanism, see function eldev-load-project-dependencies.

=== Examining dependencies

Sometimes it is useful to check what a project depends on, especially if it is not your project, just something you have checked out. There are two commands for this in Eldev.

First is dependencies (can be shortened to deps). It lists direct dependencies of the project being built. By default, it omits any built-in packages, most importantly emacs. If you want to check those too, add option -b (--list-built-ins).

Second is dependecy-tree (short alias: dtree). It prints a tree of project direct dependencies, direct dependencies of those, and so on — recursively. Like with the first command, use option -b if you want to see built-ins in the tree.

Both commands can also list additional dependencies if instructed: just specify set name(s) on the command line, e.g.:

$ eldev dependencies test

You can also check which archives Eldev uses to look up dependencies for this particular project with the following command:

$ eldev archives

=== Upgrading dependencies

Eldev will install project dependencies automatically, but it will never upgrade them, at least if you don’t change your project to require a newer version. However, you can always explicitly ask Eldev to upgrade the installed dependencies:

$ eldev upgrade

First, package archive contents will be refetched, so that Eldev knows about newly available versions. Next, this command upgrades (or installs, if necessary) all project dependencies and all additional dependencies you might have registered (see <<additional-dependencies,above>>). If you don’t want to upgrade everything, you can explicitly list names of the packages that should be upgraded:

$ eldev upgrade dash ht

You can also check what Eldev would upgrade without actually upgrading anything:

$ eldev upgrade --dry-run

{since-0-5} If you use MELPA for looking up dependencies, you can switch between Stable and Unstable using global options with the same name, i.e.:

$ eldev --unstable upgrade

Because of the incompatible version numbers that MELPA Unstable supplies, you cannot directly “upgrade” from an unstable version back to a stable one. But you can specify option -d (--downgrade) to the command:

$ eldev --stable upgrade -d

In this case Eldev will downgrade dependencies if this allows it to use more preferable package archive. (Since --stable is the default, specifying it in the command above is not really needed, it’s only mentioned for clarity.)

To install unstable version of only a specific dependency, while leaving all others at stable versions, combine --unstable with listing package names after the command, e.g.:

$ eldev --unstable upgrade dash

==== Upgrading development tools

{since-0-6} Command upgrade works not only with package dependencies, but also with common development tools used by the project during development, for example <<buttercup,Buttercup>> or <<linting,various linters>>. This works exactly the same as for project dependencies, with the only exception that the tool must be installed first. E.g., for Buttercup you need to <<testing,test>> your project at least once, so that Eldev knows about the need for this tool.

Development tools are installed from package archives hardcoded inside Eldev, regardless of which archives you have configured for your project. For example, even if you use melpa-unstable archive, Buttercup will still be installed from MELPA Stable (unless, of course, you use --unstable global option). If you need, you can switch to unstable version of the tool later:

$ eldev --unstable upgrade buttercup

[#global-cache] === Global package archive cache

{since-0-4} To avoid downloading the same packages repeatedly, Eldev employs a package archive cache. This cache is shared between all projects and <<different-emacs-versions,all Emacs versions>> on your machine. It can significantly speed up package preparation if you use a new project, test it on another Emacs version or delete project-specific cache (subdirectory .eldev) for whatever reason.

By default, downloaded packages stay cached indefinitely, while archive contents expires in one hour. However, if you use command upgrade or upgrade-self, package archive contents is always refreshed.

Cache usage is not controllable from command line. However, you can customize it somewhat in ~/.eldev/config. Variable eldev-enable-global-package-archive-cache lets you disable the global cache outright. Using eldev-global-cache-archive-contents-max-age you can adjust how long cached copies of archive-contents stay valid.

[#loading-modes] == Loading modes

In Eldev the project’s package and its local dependencies have loading modes. This affects exactly how the package (that of the project or of its local dependency) becomes loadable by Emacs.

Default loading mode is called as-is. It means the directory where project (or local dependency) is located is simply added to Emacs varible load-path and normal Emacs loading should be able to find required features from there on. This is the fastest mode, since it requires no preparation and in most cases is basically what you want during development.

However, users won’t have your project loaded like that. To emulate the way that most of the people will use it, you can use loading mode packaged. In this mode, Eldev will first build a package out of your project (or local dependency), then install and activate it using Emacs’ packaging system. This is quite a bit slower than as-is, because it involves several preparation steps. However, this is almost exactly the way normal users will use your project after e.g. installing it from MELPA. For this reason, this mode is recommended for <<continuous-integration,continuous integration>> and other forms of automated testing.

Other modes include byte-compiled and source. In these modes loading is performed just as in as-is mode, but before that Eldev either byte-compiles everything or, vice-versa, removes .elc files.

So, after discussing the loading modes, let’s have a look at how exactly you tell Eldev which one to use.

For the project itself, this is done from the command line using global option --loading (or -m) with its argument being the name of the mode. Since this is supposed to be used quite frequently, there are also shortcut options to select specific modes: --as-is (or -a), --packaged (-p), --source (-s) or --byte-compiled (-c). For example, the following command will run unit-tests in the project, having it loaded as an Emacs package:

$ eldev -p test

Remember, that as everything in Eldev, this can be customized. E.g. if you want to run your project byte-compiled by default, add this to your Eldev-local:

[source]

(setf eldev-project-loading-mode 'byte-compiled)

For local dependencies the mode can be chosen when calling eldev-use-local-dependency. For example:

[source]

(eldev-use-local-dependency "~/barlib" 'packaged)

As mentioned above, loading mode defaults to as-is.

There are a few other loading modes useful only for certain projects. You can always ask Eldev for a full list:

$ eldev --list-modes

=== Autoloads

{since-0-6} Autoloaded functions of installed Elisp packages can be accessed without a require form. To simplify development, Eldev provides the same functionality for projects regardless of loading mode, as long as file PACKAGE-autoloads.el exists. This might look like an unwieldy requirement, but luckily there is <<autoloads-plugin,a plugin>> for building the file and keeping it up-to-date as necessary. The reason this is not enabled by default is that many projects — especially those not providing user-visible functionality, or those that consist of a single file — don’t have any autoloading functions or other forms.

Local dependencies also have their autoloads activated regardless of loading mode. If the autoloads file is kept up-to-date using <<autoloads-plugin,the plugin>>, Eldev will take care to do this as needed in local dependencies too.

== Build system

Eldev comes with quite a sofisticated build system. While by default it only knows how to build packages, byte-compile .el files and make .info from .texi, you can extend it with custom builders that can do anything you want. For example, generate resource files that should be included in the final package.

The main command is predictably called build. There are also several related commands which will be discussed in the next sections.

=== Targets

Build system is based on targets. Targets come in two kinds: real and virtual. First type of targets corresponds to files — not necessarily already existing. When needed, such targets get rebuilt and the files are (re)generated in process. Targets of the second type always have names that begin with “:” (like keywords in Elisp). Most import virtual target is called :default — this is what Eldev will build if you don’t request anything explicitly.

To find all targets in a project (more precisely, its main <<target-sets,target set>>):

$ eldev targets

Project’s targets form a tree. Before a higher-level target can be built, all its children must be up-to-date, i.e. built first if necessary. In the tree you can also see sources for some targets. Those can be distinguished by lack of builder name in brackets. Additionally, if output is colored, targets have special color, while sources use default text color.

Here is how target tree looks for Eldev project itself (version may be different and more targets may be added in future):

:default
    bin/eldev  [SUBST]
        bin/eldev.in
:package
    dist/eldev-0.1.tar  [PACK]
        bin/eldev  [repeated, see above]
        eldev-ert.el
        eldev-util.el
        eldev.el
:compile
    eldev-ert.elc  [ELC]
        eldev-ert.el
    eldev-util.elc  [ELC]
        eldev-util.el
    eldev.elc  [ELC]
        eldev.el
:package-archive-entry
    dist/eldev-0.1.entry  [repeated, see ‘dist/eldev-0.1.tar’ above]

And a short explanation of various elements:

:default, :package, :compile etc.::

Virtual targets.  The ones you see above are typical, but there
could be more.

bin/eldev, dist/eldev-0.1.tar, eldev-ert.elc etc.::

Real targets.

SUBST, PACK, ELC::

Builders used to generate target.  Note that virtual targets never
have builders.  `SUBST` is not a standard builder, it is defined
in file `Eldev` of the project.

bin/eldev.in, eldev-ert.el etc.::

Sources for generating targets.  Certain targets have more than
one source file.  Also note how targets can have other targets as
their sources (`bin/eldev` is both a target on its own and a
source for `dist/eldev-0.1.tar`).

[repeated \...]::

To avoid exponential increase in tree size, Eldev doesn’t repeat
target subtrees.  Instead, only root target of a subtree is
printed.

==== Target cross-dependencies

FIXME

[#target-sets] ==== Target sets

Eldev groups all targets into sets. Normally, there are only two sets called main and test, but you can define more if you need (see variable eldev-filesets). For example, if your project includes a development tool that certainly shouldn’t be included in project’s package, it makes sense to break it out into a separate target set.

Target sets should be seen only as ways of grouping targets together for the purpose of quickly enumerating them. Two targets in the same set can be completely independent from each other. Similarly, targets from different sets can depend on each other (provided this doesn’t create a circular dependency, of course). For example, targets in set test will often depend on those in set main, because test .el files usually require some features from main.

By default, command build operates only on main target set. You can use option --set (-s) to process a different target set. If you want to build several sets at once, repeat the option as many times as needed. Finally, you can use special name all to order Eldev to operate on all defined sets at once.

Command targets instead of the option expects set names as its arguments. For example:

$ eldev targets test

[#packaging] === Building packages

To build an Elisp package out of your project, use command package:

$ eldev package

This command is basically a wrapper over the build system, it tells the system to generate virtual target :package. However, there are a few options that can only be passed to this special command, not to underlying build.

Normally, packages are generated in subdirectory dist (more precisely, in directory specified by eldev-dist-dir variable). If needed, you can override this using --output-dir option.

By default, Eldev will use package’s self-reported version, i.e. value of “Version” header in its main .el file. If you need to give the package a different version, use option --force-version. E.g. MELPA would do this if it used Eldev.

Finally, if you are invoking Eldev from a different tool, you might be interested in option --print-filename. When it is specified, Eldev will print absolute filename of the generated package and word “generated” or “up-to-date” as the two last lines of its (stdout) output. Otherwise it is a bit tricky to find the package, especially if you don’t use --force-version option. As an optimisation, you can also reuse previous package file if Eldev says “up-to-date”.

=== Byte-compiling

You can use Eldev to byte-compile your project. Indirectly, this can be done by <<loading-modes,selecting appropriate loading mode>> for the project or its local dependencies. However, sometimes you might want to do this explicitly. For this, use command compile:

$ eldev compile

You can also byte-compile specific files:

$ eldev compile foo-util.el foo-misc.el

Eldev will not recompile .el that have up-to-date .elc versions. So, if you issue command compile twice in a row, it will say: “Nothing to do” the second time.

However, simple comparison of modification time of .el and its .elc file is not always enough. Suppose file foo-misc.el has form (require 'foo-util). If you edit foo-util.el, byte-compiled file foo-misc.elc might no longer be correct, because it has been compiled against old definitions from foo-util.el. Luckily, Eldev knows how to detect when a file requires another. You can see this in the target tree:

$ eldev targets --dependencies
[...]
:compile
    foo-misc.elc  [ELC]
        foo-misc.el
        [inh] foo-util.elc
[...]

As a result, if you now edit foo-util.el and issue compile again, both foo-util.elc and foo-misc.elc will be rebuilt.

Eldev treats warnings from Emacs’ byte-compiler just as that — warnings, i.e. they will be shown, but will not prevent compilation from generally succeeding. However, during <<continuous-integration,automated testing>> you might want to check that there are no warnings. The easiest way to do it is to use --warnings-as-errors option (-W):

$ eldev compile --warnings-as-errors

Command compile is actually only a wrapper over the generic building system. You can rewrite all the examples above using command build. If you don’t specify files to compile, virtual target :compile is built. This target depends on all .elc files in the project.

However, there is a subtle difference: for compile you specify source files, while build expects targets. Therefore, example

$ eldev compile foo-util.el foo-misc.el

above is equivalent to this command:

$ eldev build foo-util.elc foo-misc.elc

with .el in filenames substituted with .elc.

==== Byte-compiling complicated macros ====

Certain files with macros in Elisp cannot be byte-compiled without evaluating them first or carefully applying eval-and-compile to functions used in macroexpansions. Because Emacs packaging system always loads (evaluates) package files before byte-compiling them during installation, this is often overlooked.

Unlike the packaging system, Eldev by default expects that .el files can be compiled without loading them first, i.e. it expects that eval-and-compile is applied where needed. This is the default because it is much faster on certain files.

However, if your project cannot be byte-compiled without loading first and you don’t want to “fix” this, you can ask Eldev to behave like the packaging system using --load-before-compiling (-l) option:

$ eldev compile -l

Projects that can only be compiled with this setting should specify it as the default in their file Eldev:

[source]

(setf eldev-build-load-before-byte-compiling t)

You can find more information in section https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval-During-Compile.html[“Evaluation During Compilation” of Elisp manual].

==== Speed of byte-compilation ====

While not particularly important in most cases, speed of byte-compilation can become an issue in large projects, especially if they use lots of macros. Eldev tries to speed up byte-compilation by compiling the files in “correct” order.

This means that if, as above, foo-misc.el requires feature foo-util, then foo-util.el will always be byte-compiled first, so that compilation of foo-misc.el can use faster, byte-compiled versions of definitions from that file. This works even if Eldev doesn’t yet know which files require which.

When Eldev has to change the planned order of byte-compilation because of a require form, it writes an appropriate message (you need to run with option -v or -t to see it):

$ eldev -v compile
[...]
ELC      foo-misc.el
Byte-compiling file ‘foo-misc.el’...
ELC      foo-util.el
Byte-compiling file ‘foo-util.el’ early as ‘require’d from another file...
Done building “sources” for virtual target ‘:compile’

[#cleaning] === Cleaning

While cleaning is not really part of the build system, it is closely related. Cleaning allows you to remove various generated files that are the result of other commands (not only build). Command can be executed without any arguments:

$ eldev clean

In this case, it removes byte-compiled Elisp files and any .info files generated from .texi/.texinfo if you have those in your project.

In general case, you can specify name one or more cleaners as command arguments. All supported cleaners can be found using option --list-cleaners (-L). Here is a short list of some of the more useful ones:

.eldev::

Delete Eldev’s cache, i.e. subdirectory `.eldev` for this project.

distribution (or dist)::

Delete `dist` subdirectory; useful after <<packaging,building
project’s package>>.

test-results (or tests)::

Forget <<test-results,previous test results>>, for ERT.

global-cache::

Remove contents of the <<global-cache,global package archive
cache>>.  This can be executed from any directory.

all (or everything)::

Run all available cleaners.  Some cross-project data may still be
retained (currently, only the global package archive cache), that
can be cleaned only by explicitly mentioning it.

Cleaners executed by default are called .elc, .info and info-dir. Normally, they delete their targets in all <<target-sets,target sets>> at once. However, you can limit them to main, test and so on set with option -s (--set), e.g. command:

$ eldev clean -s test

would delete all byte-compiled test files.

You can also specify option -n (--dry-run) to see what would be deleted, without actually deleting it.

[#testing] == Testing

Eldev has built-in support for running regression/unit tests of your project. Currently, Eldev supports only {uri-ert}[ERT] and {uri-buttercup}[Buttercup] testing frameworks. Leave a feature request in the issue tracker if you are interested in a different library.

Simply executing

$ eldev test

will run all your tests. By default, all tests are expected to be in files named test.el, tests.el, +*-test.el+, +*-tests.el+ or in test or tests subdirectories of the project root. But you can always change the value of eldev-test-fileset variable in the project’s Eldev as appropriate.

By default, the command runs all available tests. However, during development you often need to run one or a few tests only — when you hunt a specific bug, for example. Eldev provides two ways to select which tests to run.

First is by using a selector (<<frameworks,framework-specific>>, this example is for <<ert,ERT>>):

$ eldev test foo-test-15

will run only the test with that specific name. It is of course possible to select more than one test by specifying multiple selectors: they are combined with ‘or’ operation. You can use any selector supported by the testing framework here, see its documentation.

The second way is to avoid loading (and executing) certain test files altogether. This can be achieved with --file (-f) option:

$ eldev test -f foo.el

will execute tests only in file foo.el and not in e.g. bar.el. You don’t need to specify directory (e.g. test/foo.el); for reasons why, see <<filesets,explanation of Eldev filesets below>>.

Both ways of selecting tests can be used together. In this case they are combined with ‘and’ operation: only tests that match selector and which are defined in a loaded file are run.

When a test is failing, a backtrace of the failure is printed. You can affect its readability and completeness using options -b (--print-backtrace, the default) and -B (--omit-backtraces). The first option accepts your screen width as an optional parameter; backtrace lines get cut to the specified width. (Since 0.7 this can also be specified as a global option that additionally affects all other backtraces that are printed by Eldev.) Special value of 0 (the default in Eldev) disables truncation of backtrace lines. Second option, -B, is surprisingly useful. In many cases backtraces don’t actually give any useful information, especially when the tests contain only a single assertion, and only clutter the output. If you have different preferences compared to Eldev, you can customize variable eldev-test-print-backtraces in file ~/.eldev/config.

How exactly tests are executed depends on test runner. If you dislike the default behavior of Eldev, you can choose a different test runner using --runner (-r) option of test command; see the list of available test runners with their descriptions using --list-runners option. If you always use a different test runner, it is a good idea to set it as the default in file ~/.eldev/config. Finally, you can even write your own runner.

[#frameworks] === Frameworks

As stated above, Eldev supports {uri-ert}[ERT] (Emacs built-in) and {uri-buttercup}[Buttercup] testing frameworks. Normally, you don’t need to specify which framework the project uses, as the tool can autodetect that. But in rare cases you may need to set variable eldev-test-framework to either 'ert or 'buttercup, as appropriate. You also don’t need to declare testing package as <<additional-dependencies,an extra dependency>>: Eldev will install it itself when needed.

Eldev tries to provide uniform command line interface to the supported frameworks, but of course there are many differences between them.

[#ert] ==== ERT

{uri-ert}[ERT] is the “default” testing framework and also an Emacs built-in. This means that no additional packages need to be installed and the framework is available on all non-ancient Emacs versions (at least all Eldev itself supports).

All functionality of test command works with ERT.

[#buttercup] ==== Buttercup

{since-0-2} {uri-buttercup}[Buttercup] is a behavior-driven development framework for testing Emacs Lisp code. Its support in Eldev has some limitations. On the other hand, certain functionality is not supported by the library itself, and e.g. its bin/buttercup script also doesn’t provide similar features.

When using Buttercup, selectors are patterns from {uri-buttercup-rt}[the library’s documentation]. I.e. they are regular expressions in Emacs syntax, and only tests with names matching at least one of the specified selectors/patterns are executed.

Things that won’t work with Buttercup at the moment:

  • option --stop-on-unexpected (-s);
  • specifying screen width with option --print-backtraces (-b): it will always work as if 80 was specified.

Unlike <<ert,ERT>>, Buttercup also has no special selectors that base on the previous run’s results.

=== Loading test files

{since-0-5} There appears to be two common ways of using tests: 1) they are loaded from project root; 2) subdirectory test/ (or similar) in the project is added to load-path. Eldev supports both. First one is the default, since it doesn’t require anything in addition.

To better understand the second way, imagine your project structure is like this:

.... tests/ test-helper.el test-my-project.el ....

and file test-my-project.el includes a form (require 'test-helper). Naturally, this setup will work only if subdirectory tests/ is in load-path by the point tests are executed. To instruct Eldev that your project needs this, add the following to file Eldev:

[source]

(eldev-add-loading-roots 'test "tests")

where 'test is the command name and "tests" is the name of the subdirectory that should serve as additional loading root. In principle, loading roots can also be used for other commands too, just like <<additional-dependencies,extra dependencies>>.

If you want to switch to the first way and avoid special forms in file Eldev, replace (require 'test-helper) with (require 'tests/test-helper).

[#test-results] === Reusing previous test results

<<ert,ERT>> provides a few selectors that operate on tests’ last results. Even though different Eldev executions will run in different Emacs processes, you can still use these selectors: Eldev stores and then loads last results of test execution as needed.

For example, execute all tests until some fails (-s is a shortcut for --stop-on-unexpected):

$ eldev test -s

If any fails, you might want to fix it and rerun again, to see if the fix helped. The easiest way is:

$ eldev test :failed

For more information, see {uri-ert-sel}[documentation on ERT selectors] — other “special” selectors (e.g. :new or :unexpected) also work.

[#testing-simplifications] === Testing command line simplifications

When variable eldev-dwim (“do what I mean”) is non-nil (as by default), Eldev supports a few simplifications of the command line to make testing even more streamlined.

  • For all frameworks: any selector that ends in .el is instead treated as a file pattern. For example:

-- $ eldev test foo.el

will work as if you specified -f before foo.el.

  • For <<ert,ERT>>: any symbol selector that doesn’t match a test name is instead treated as regular expression (i.e. as a string). For example:

-- $ eldev test foo

will run all tests with names that contain foo. You can achieve the same result with ‘strict’ command line (see also ERT selector documentation) like this:

$ eldev test \"foo\"

--

If you dislike these simplifications, set eldev-dwim to nil in ~/.eldev/config.

[#linting] == Linting

{since-0-2} It might be useful to ensure that your source code follows certain standards. There are many programs called linters that can help you with this. Several of them are also supported by Eldev and can be executed using the tool.

In its simplest form lint command will execute all supported linters and let them loose on your source code in main target set:

$ eldev lint

You don’t need to install anything additionally: Eldev will download and use required packages itself. Because of this, first linting in a project might take a while to prepare, but later the downloaded linters will be reused.

Currently, Eldev knows and uses the following linters:

  • Emacs built-in checkdoc. Verifies documentation strings of your functions, variables and so on for various style errors.
  • {uri-package-lint}[package-lint], which detects erroneous package metadata, missing dependencies and much more.
  • {uri-relint}[relint] that can detects errors in regular expression strings in your source code.
  • {since-0-6} {uri-elisp-lint}[elisp-lint] that checks Elisp code for various errors — it is even more versatile than package-lint and actually optionally includes it.

In future, more linters can gain special treatmeant from Eldev (you can also leave a feature request in the issue tracker). The full list can always be found using command eldev lint --list.

Running all the linters at once is not always what you want. In such a case you can just specify name (or several) of the linter you want on the command line:

$ eldev lint doc

Names can be simplified by dropping words “check” and “lint” from them. It is also possible to explicitly direct linters at certain files, rather than verifying all at once:

$ eldev lint re -f foo.el

Like with <<testing-simplifications,testing>>, you can omit -f (--file) option above as long as variable eldev-dwim is non-nil.

Some projects, however, may decide to follow advices of certain linters, but not the others. You can explicitly tell Eldev about project’s policy by adjusting one or more of variables eldev-lint-default, eldev-lint-default-excluded and eldev-lint-disabled in file Eldev. All of these variables affect which linters exactly Eldev starts when their names are not specified explicitly.

Command lint sets Eldev’s exit status to non-zero if there is at least one warning from any requested linter. This simplifies using linting in <<continuous-integration,continuous integration>> should you want to do that.

== Quickly evaluating expressions

It is often useful to evaluate Elisp expressions in context of the project you develop — and probably using functions from the project. There are two commands for this in Eldev: eval and exec. The only difference between them is that exec doesn’t print results to stdout, i.e. it assumes that the forms you evaluate produce some detectable side-effects. Because of this similarity, we’ll consider only eval here.

The basic usage should be obvious:

$ eldev eval "(+ 1 2)"

Of course, evaluating (+ 1 2) form is not terribly useful. Usually you’ll want to use at least one function or variable from the project. However, for that you need your project not only to be in load-path (which Eldev guarantees), but also required. Luckily, you don’t have to repeat (require 'my-package) all the time on the command line, as Eldev does this too, so normally you can just run it like this:

$ eldev eval "(my-package-function)"

What Eldev actually does is requiring all features listed in variable eldev-eval-required-features. If value of that variable is symbol :default, value of eldev-default-required-features is taken instead. And finally, when value of the latter is symbol :project-name, only one feature with the same name as that of the project is required. In 95% of the cases this is exactly what you need. However, if the main feature of the project has a different name, you can always change the value of one of the mentioned variables in file Eldev.

It can also make sense to change the variable’s value in Eldev-local if you want certain features to always be available for quick testing.

== Running Emacs

Sometimes you want to run Emacs with just your project installed and see how it works without any customization. You can achieve this in Eldev easily:

$ eldev emacs

This will spawn a separate Emacs that doesn’t read any initialization scripts and doesn’t have access to your usual set of installed packages, but instead has access to the project being built with Eldev — and its dependencies, of course. Similar as with eval and exec commands, features listed in variable eldev-emacs-required-features are required automatically.

You can also pass any Emacs options through the command line. For example, this will visit file foo.bar, which is useful if your project is a mode for .bar files:

$ eldev emacs foo.bar

See emacs --help for what you can specify on the command line.

When issued as shown above, command emacs will pass the rest of the command line to Emacs, but also add a few things on its own. First, it adds everything from the list eldev-emacs-default-command-line, which disables ~/.emacs loading and similar things. Second, it transfers variables listed in eldev-emacs-forward-variables to the child process (this is done in order to keep <<project-isolation,project isolation>> promises). Third, adds --eval arguments to require the features as described above. And only after that comes the actual command line you specified.

Occasionally you might not want this behavior. In this case, prepend -- to the command line — then Eldev will pass everything after it to the spawned Emacs as-is (with the exception of still transferring variables listed in eldev-emacs-forward-variables). Remember that you will likely need to pass at least -q (--no-init-file) option to Emacs, otherwise it will probably fail on your ~/.emacs since it will not see your usual packages. To illustrate:

$ eldev emacs -- -q foo.bar

[#different-emacs-versions] == Executing on different Emacs versions

Since Eldev itself is an Elisp program, version of Emacs you use can affect any aspect of execution — even before it gets to running something out of your project. Therefore, inside its “cache” directory called .eldev, the utility creates a subdirectory named after Emacs version it is executed on. If it is run with a different Emacs, it will not use dependencies or previous test results, but rather install or recompute them from scratch.

Normally, Eldev uses command emacs that is supposed to be resolvable through $PATH environment variable. However, you can always tell it to use a different Emacs version by setting either ELDEV_EMACS or just EMACS in the environment, e.g.:

$ EMACS=emacs25 eldev eval emacs-version

This is especially useful for testing your project with different Emacs versions.

Remember, however, that Eldev cannot separate byte-compiled files (.elc) from sources. From documentation of byte-compile-dest-file-function:


Note that the assumption that the source and compiled files are found in the same directory is hard-coded in various places in Emacs.


Therefore, if you use byte-compilation and switch Emacs versions, don’t forget to clean the directory.

[#continuous-integration] == Continuous integration

Because of Eldev’s trivial installation and built-in support for testing, it is a suitable tool for use on continuous integration servers. But of course this only applies if the test framework your project uses is already supported (currently <<ert,ERT>> and <<buttercup,Buttercup>>).

[#github-workflows] === GitHub workflows

The easiest option for continuous integration for GitHub-hosted projects are {uri-github-wflows}[GitHub workflows], as this doesn’t involve using a 3rd-party service. Probably most of Elisp projects can take advantage of this, since GitHub appears to be the most popular hosting for Elisp projects.

Workflow definition files for GitHub are somewhat more verbose than for <<travis-ci,Travis CI>>, but ultimately not really more complicated. The easiest way to install Emacs binary of appropriate version is to use {uri-setup-emacs}[purcell/setup-emacs] action (which internally uses {uri-nix-emacs}[nix-emacs-ci]). Since {uri-evm}[EVM] seems tuned to Ubuntu Trusty (i.e. what Travis CI provides), it is likely unsuitable for GitHub workflows.

There is a short shell script that installs Eldev itself for use on GitHub runners. Modifying $PATH there is a bit tricky, so you probably should just go with the script, as demonstrated below.

A basic workflow file (you can e.g. name it .github/workflows/test.yml) would look something like this:

.... name: CI

on: push: paths-ignore: - '.md' pull_request: paths-ignore: - '.md'

jobs: test: runs-on: ubuntu-latest strategy: matrix: emacs_version: # Add more lines like this if you want to test on different Emacs versions. - 26.3

steps:
- name: Set up Emacs
  uses: purcell/[email protected]
  with:
    version: ${{matrix.emacs_version}}

- name: Install Eldev
  run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh

- name: Check out the source code
  uses: actions/[email protected]

- name: Test the project
  run: |
    eldev -p -dtT test

....

Eldev’s terminal autorecognition doesn’t work on GitHub machines (unlike e.g. on Travis CI). If you want colored output from Eldev, you need to explicitly enable it using -C (--color) global option.

[#travis-ci] === Travis CI

{uri-travis}[Travis CI] is perhaps the most used continuous integration service for Elisp code, at least until the addition of <<github-workflows,GitHub workflows>>. The largest problem on Travis CI is to install Emacs binary of the desired version. Luckily, there are tools that can be used for this: at least {uri-evm}[EVM] and {uri-nix-emacs}[nix-emacs-ci].

==== EVM

One of the tools to install Emacs is {uri-evm}[EVM]. Steve Purcell (the author of nix-emacs-ci) mentions “various issues” he has had with it, however many projects use it. Apparently, you need to fix Ubuntu distribution used at Travis CI to Trusty for EVM-provided binaries. Also note that EVM provides binaries only for Linux, so if you want test on macOS too, nix-emacs-ci is a better choice.

If you also want to try it, Eldev provides a simple script specifically for use on Travis CI that installs Eldev and EVM in one go. Here is a simple project-agnostic .travis.yml file that you can use as a basis:

.... language: emacs-lisp dist: trusty

env:

Add more lines like this if you want to test on different Emacs versions.

  • EVM_EMACS=emacs-26.3-travis

install:

script:

  • eldev -p -dtT test ....

==== nix-emacs-ci

A newer tool to install Emacs is {uri-nix-emacs}[nix-emacs-ci]. Using it is easy: define environment variable $EMACS_CI with the desired Emacs version and curl a single shell script — whether on Linux or macOS. With one more line you can also install Eldev. It appears to be slower than EVM, but for continuous integration that’s not terribly important.

A basic .travis.yml would look like this:

.... language: nix

env:

Add more lines like this if you want to test on different Emacs versions.

  • EMACS_CI=emacs-26-3

install:

script:

  • eldev -p -dtT test ....

[#circle-ci] === CircleCI

Another frequently used service is {uri-circle}[CircleCI]. I don’t know that much about it, presumably {uri-nix-emacs}[nix-emacs-ci] can be used to install Emacs on it. Some projects successfully use {uri-docker}[Docker] images.

Regardless of how you install Emacs, adding Eldev is yet another one-liner. It is handy to use, because propagating $PATH modifications between different commands on CircleCI is somewhat non-obvious. To use it, add the following lines in the relevant place in file .circleci/config.yml:

.... ... - run: name: Install Eldev command: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/circle-eldev > x.sh && source ./x.sh ....

=== Script commands

Once you have Emacs with Eldev set up on the continuous integration server of your choice, it is time to actually test your project. The most basic command is, naturally, eldev test. You might want to add a few options to both make project loading more similar to that typical for your users and Eldev’s output more informative:

$ eldev -p -dtT test

To make sure that your project byte-compiles cleanly, use the following command:

$ eldev -dtT compile --warnings-as-errors

Or maybe even this, if you want to make sure that test .el files also can be byte-compiled without warnings (this can sometimes catch more problems):

$ eldev -dtT compile --set all --warnings-as-errors

You can also enforce conformance to certain coding standards by adding an invocation of lint to the script part. Remember, however, that most linters are continuously being developed. Even if a linter finds your source warning-free today, it might detect problems tomorrow. relint is probably one of the “safer” linters in this regard:

$ eldev -dtT lint re

[#debugging-features] == Debugging features

Eldev comes with lots of different options and other features that can help you debug problems both in your project, Eldev itself or your Eldev scripts.

  • Global options -t (--trace), -v (--verbose) and -q (--quiet) control the amount of output Eldev generates. The first one makes Eldev extra verbose, helping you to understand what it is doing and at which step something goes wrong.

  • Global option -d (--debug) makes Eldev print backtrace if it dies with a Elisp signal (except certain well-defined and explained errors like missing dependency).

  • {since-0-3} Global option -Q (--backtrace-on-abort) makes Eldev print backtrace if it is aborted with ^C. This is useful if your project freezes or has very bad performance, and you want to figure out where exactly this happens.

  • {since-0-8} Global option -b (--backtrace) lets you adapt backtraces to your screen width and thus make them more readable at the expense of completeness (by default, Eldev doesn’t truncate backtrace lines). It is a good idea to change the default in file .eldev/config.

  • Global option -T (--time) prepends timestamps to all lines of Eldev output, making it easier to spot performance problems.

  • Command prepare can be used to install all project dependencies — and thus check if they and package archives are specified correctly — without doing anything else.

  • Commands deps (dependencies) and dtree (dependency-tree) can be used to display list or tree of project dependencies, which is especially useful for large projects unfamiliar to you.

  • For many errors, Eldev will print additional hints (unless you specify option --quiet). For example: if an error happens during evaluating file Eldev, the tool will mention this; if a dependency cannot be installed, Eldev will mention what required this dependency (can be non-obvious in larger packages).

  • While not a direct feature of Eldev itself, file Eldev-local provides a good place to install temporary advices, overwrite Emacs functions etc. in the process of debugging certain problems.

  • You can temporarily add calls to eldev-warn, eldev-backtrace and other Eldev functions to the tests in your project to provide additional output. But it is a good idea to do this only while debugging and avoid committing such changes.

== Plugins

{since-0-3} Plugins are activatable extensions to Eldev functionality. They provide features that are not needed for most projects and are therefore not enabled by default. However, enabling a plugin is trivial — just add line:

[source]

(eldev-use-plugin 'PLUGIN-NAME)

to file Eldev of your project. For example:

[source]

(eldev-use-plugin 'autoloads)

As for other configuration, you can also do it in Eldev-local or <<setup-procedure,other places>>.

In future, plugins may become externally-managed and “detached” from Eldev itself (create an issue if you are interested). For now, however, Eldev provides two built-in plugins.

You can check if a project has any plugins activated — and documentation for those plugins:

$ eldev plugins

Run Eldev in quiet mode (-q) to get only the list, without the long documentation:

$ eldev -q plugins

Remember that if a project activates a plugin in a non-standard way, for example from <<hooks,a hook>>, command plugins will not see it.

There is currently no way to list all available plugins. However, as of yet there are only two plugins anyway.

[#autoloads-plugin] === autoloads

{since-0-6} A plugin that enables automatic collection of functions and other forms marked with ;;;###autoload cookie in project’s .el files. It tries to behave exactly the same as for installed Elisp packages, so that there are no differences between development and installed versions of the project.

The plugin is not on by default because many projects don’t use autoloading functionality at all and having file PACKAGE-autoloads.el magically appear all the time in them would be annoying.

To have autoloads automatically collected in your project, just activate the plugin: add form (eldev-use-plugin 'autoloads) to the project’s file Eldev. You don’t need any additional steps to instruct Eldev how to use the generated file. In fact, it is able to do this even without the plugin: the plugin only takes cares to build and update the file as necessary.

If the plugin is activated, you can see new target :autoloads in the output of targets command. In addition to being built by default, this file is also generated whenever Eldev needs to load the project: for commands test, eval, exec and emacs. Finally, the file is also registered as a dependency to all .elc targets in the project; this way, byte-compiling always has access to up-to-date list of autoloaded functions.

This plugin can also be activated in projects you use as <<local-dependencies,local dependencies>> for other projects. Eldev knows how to keep the autoloads file up-to-date in all local dependencies, regardless of their loading mode.

[#undercover-plugin] === undercover

{since-0-3} This built-in plugin provides integration with {uri-undercover}[undercover] tool that generates coverage reports for your tests. It is active only for <<testing,command test>>. By default, behavior of the tool is unaltered (with the exception that reports are not merged), so effectively it will do nothing unless run on a supported <<continuous-integration,continuous integration>> server.

To have your project’s code coverage statistics automatically gathered during continuous integration, all you need to do is:

. Activate the plugin: add (eldev-use-plugin 'undercover) to your project’s file Eldev.

. Make sure that command test is executed during automated testing (e.g. in file .travis.yml) in as-is, source or built-source <<loading-modes,loading mode>>. If you want, you can run it again additionally in packaged mode.

The plugin adds two options for command test: --undercover (-u) and --undercover-report (-U). First option can be used to configure the plugin and the tool, the second — to change report filename. Value for the option -u should be a comma and/or space-separated list of any of the following flags:

auto, on (always), off (never)::

whether to generate the report; default value is `auto`;

coveralls, simplecov, codecov, text::

format of the report to generate; default is `coveralls`;

merge, restart::

whether to merge with existing report; note that by default report
is _restarted_, i.e. existing report file is deleted;

send, dontsend::

whether to send the generated report to
{uri-coveralls}[coveralls.io] (only for the suitable format);
default is to send.

Additionally, when eldev-dwim is non-nil, certain flags can affect each other:

  • if report format is not set explicitly, it is derived from extension of report filename if possible: .json for simplecov format, .txt or .text for a text report; codecov format cannot be set this way, currently;
  • when requested format is not coveralls, report is always generated unless auto or off (never) is specified explicitly.

Based on the above, easiest way to generate a local coverage report is something like this:

$ eldev test -U simplecov.json

Full help for the plugin can always be checked by running eldev plugins in a project with the plugin activated.

[#filesets] == Filesets

Filesets are lists of rules that determine a collection of files inside given root directory, usually the project directory. Similar concepts are present in most build tools, version control systems and some other programs. Filesets in Eldev are inspired by Git.

Important examples of filesets are variables eldev-main-fileset, eldev-test-fileset and eldev-standard-excludes. Default values of all three are simple filesets, but are not actually restricted to those: when customizing for your project you can use any valid fileset as a value for any of these variables. However, for most cases simple filesets are all that you really need.

=== Simple filesets

From Lisp point of view, a simple fileset is a list of strings. A single-string list can also be replaced with that string. The most important filesets are eldev-main-fileset and eldev-test-fileset. Using them you can define which .el files are to be packaged and which contain tests. Default values should be good enough for most projects, but you can always change them in file Eldev if needed.

Each rule is a string that matches file path — or its part — relative to the root directory. Path elements must be separated with a slash (/) regardless of your OS, to be machine-independent. A rule may contain glob wildcards (+*+ and ?) with the usual meaning and also double-star wildcard (+**+) that must be its own path element. It stands for any number (including zero) of nested subdirectories. Example:

foo/**/bar-*.el

matches foo/bar-1.el and foo/x/y/bar-baz.el.

If a rule starts with an exclamation mark (!), it is an exclusion rule. Files that match it (after the mark is stripped) are excluded from the result. Other (“normal”) rules are called inclusion rules.

Typically, a rule must match any part of a file path (below the root, of course). However, if a rule starts with / or ./ it is called anchored and must match beginning of a file path. For example, rule ./README matches file README in the root directory, but not in any of its subdirectories.

If a rule matches a directory, it also matches all of the files the directory contains (with arbitrary nesting level). For example, rule test also matches file test/foo/bar.el.

A rule that ends in a slash directly matches only directories. But, in accordance to the previous paragraph, also all files within such directories. So, there is a subtle difference: a rule test/ won’t match a file named test, but will match any file within a directory named test.

Finally, note a difference with Git concerning inclusions/exclusions and subdirectories. Git manual says: “It is not possible to re-include a file if a parent directory of that file is excluded.” Eldev filesets have no such exceptions.

=== Composite filesets

Eldev also supports composite filesets. They are built using common set/logic operations and can be nested, i.e. one composite fileset can include another. There are currently three types:

(:and ELEMENT\...)::

A file matches an :and fileset if and only if it matches every of its ELEMENT filesets.

(:or ELEMENT\...)::

A file matches an :or fileset if and only if it matches at least one of its ELEMENT filesets.

(:not NEGATED)::

A file matches a :not fileset when it doesn’t match its NEGATED fileset and vice versa.

=== Evaluated filesets

Finally, some parts of filesets — but not elements of simple filesets! — can be evaluated. An evaluated element can be a variable name (a symbol) or a form. When matching, such element will be evaluated once, before eldev-find-files or eldev-filter-files start actual work.

Result of evaluating such an expression can be an evaluated fileset in turn — Eldev will keep evaluating elements until results finally consist of only simple and composite filesets. To prevent accidental infinite loops, there is a limit of eldev-fileset-max-iterations on how many times sequential evaluations can yield symbols or forms.

Example of an evaluated fileset can be seen from return value of eldev-standard-fileset function. E.g.:

[source]

(eldev-standard-fileset 'main) => (:and eldev-main-fileset (:not eldev-standard-excludes))

As the result contains references to two variables, they will be evaluated in turn — and so on, until everything is resolved.

[#extending-eldev] == Extending Eldev

Eldev is written to be not just configurable, but also extensible. It makes perfect sense to have additional code in file Eldev — if your project has uncommon building steps. And also in ~/.eldev/config — if you want a special command for your own needs, for example. Or maybe in Eldev-local — if you need something extra only for one specific project that you maintain.

[#hooks] === Hooks

Eldev defines several hooks executed at different times (more might be added later).

eldev-executing-command-hook::

Run before executing any command.  Command name (as a symbol) is
passed to the hook’s functions as the only argument.  This is
always the “canonical” command name, even if it is run using an
alias.

eldev-COMMAND-hook::

Run before executing specific command, functions have no
arguments.  Eldev itself uses it (i.e. in its file `Eldev`) to
print a disclaimer about its fairly slow tests.

{since-0-2} eldev-load-dependencies-hook::

Executed after successfully loading dependencies.  Functions are
called with arguments `TYPE` and `ADDITIONAL-SETS`.  `TYPE` is
either `t` if the project is being loaded for actual use, symbol
`load-only` if it is loaded only for side effect (e.g. to build a
tree of its dependencies), and `nil` if invoked from
`eldev-load-extra-dependencies` (i.e. if the project is not being
loaded at all: only some additional sets).  The second is a list
of <<additional-dependencies,additional dependency sets>>.

{since-0-6} eldev-before-loading-dependencies-hook::

Similar to the previous hook, but called _before_ dependencies are
loaded.  Function arguments are the same.

{since-0-1-1} eldev-build-system-hook::

Hook executed whenever build system is used.  This is useful since
at least commands `build`, `compile` and `package` invoke the
build system: it would be impractical to add the same function to
all three hooks.

{since-0-2} eldev-test-FRAMEWORK-hook::

Called immediately before executing tests with given framework
(<<ert,ERT>> or <<buttercup,Buttercup>>).  Functions on the hook
get passed `SELECTORS` as the only argument.  At this point
project dependencies and additional set `test` will have been
loaded already, so functions can `require` features from the
project.

=== Writing builders

Eldev build system provides standard builders that cover all basic needs of Elisp packages. However, some projects have uncommon build steps. Instead of writing custom shell scripts, you can integrate them into the overall build process — which also simplifies further development.

An example of a project with additional build steps is Eldev itself. Its executable(s) are combined from executable template that is OS-specific and a common Elisp bootstrapping script. For example, bin/eldev is generated from files bin/eldev.in and bin/bootstrap.el.part. However, only the first file counts as the source; see how function eldev-substitute works.

There is a simple builder for this in file Eldev of the project:

[source]

(eldev-defbuilder eldev-builder-preprocess-.in (source target) :short-name "SUBST" :message source-and-target :source-files "*.in" :targets (".in" -> "") :collect ":default" :define-cleaner (eldev-cleaner-preprocessed "Delete results of preprocessing `.in' files. This is specific to Eldev itself." :aliases prep) (let ((modes (file-modes target))) (eldev-substitute source target) (when (or modes (string-prefix-p "bin/" target)) (set-file-modes target (or modes #o755)))))

Here eldev-defbuilder is a macro much like defun. It defines an Elisp function named eldev-builder-preprocess-.in and registers it with parameters (the keyword lines before the body) as an Eldev builder. Predictably, list (source target) specifies function arguments.

Let’s skip the keywords for a bit and have a look at the body. It works exactly like in a normal Elisp function. Its job is to generate target from source using builder-specific means. This particular builder calls function eldev-substite that does the actual work (this function is available also to your project, should you need it). But your builders could do whatever you want, including launching external processes (C/C++ compiler, a Python script, etc.) and using anything from Elisp repertoire. Note that return value of the body is ignored. If building the target fails, builder should signal an error.

Now back to the keyword parameters. As you can see, they all have a name and exactly one value after it. First comes parameter :short-name. It specifies what you see in the target tree of the project, i.e. builder’s name for the user. It is not required; without it Eldev would have used preprocess-.in as user-visible name.

Next parameter is :message. It determines what Eldev prints when the builder is actually invoked. For example, when byte-compiling, you’d see messages like this:

ELC      some-file.el

That’s because byte-compiling builder has its :message set to source (the default). Other valid values are target and source-and-target (as in the example). Both source and target can be pluralized (i.e. sources-and-target is also a valid value), but singular/plural is not important in this case as both work identically. Finally, value of :message can be a function, in which case it is called with the same arguments as the builder itself and should return a string.

Value of :source-files parameter must be a <<filesets,fileset>>. In the above example, fileset consists of only one simple rule — which is actually enough in most cases, — but it could also be much more complicated. All files that match the fileset and do not match eldev-standard-excludes will be processed using this builder.

Parameter :targets defines the rule used to construct target names out of sources matched by :source-files. There are several ways to define this rule, we’ll consider them in <<target-rules,their own subsection>>.

Keyword :collect determines how targets generated by this builder are “collected” into virtual targets. In the example all such targets are simply added to the virtual target :default. However, here too we have several other possibilities, which will be described <<collecting-virtual-targets,later>>.

Finally, keyword :define-cleaner provides a simple way of linking builders with the <<cleaning,cleaning system>>.

Another important keyword is :type. It is not used here only because the example builder is of the default and most common type that generates one target for each source file. All possible types are: one-to-one (the default), one-to-many (several targets from one source file), many-to-one and many-to-many. If you write a builder of a non-default type, be aware that it will be called with a list of strings instead of a single string as one or both of its arguments, as appropriate. You should probably also name them in plural in the definition in this case, to avoid confusion.

[#target-rules] ==== Target rules

Target rules define which target(s) will be built from given source(s). There are several ways to define a target rule. Yet more can be added in the future as real-world needs accumulate.

TARGET::

All the sources will be passed together as a list to the builder
to generate one `TARGET`.  This is suitable for `many-to-one`
builders.

(TARGET-1 [TARGET-2 [\...]])::

Build several `TARGETS` out of all the sources.  This is for
`many-to-many` and `one-to-many` builders.

(SOURCE-SUFFIX \-> TARGET-SUFFIX)::

Build target name from source name by replacing filename suffixes.
`SOURCE-SUFFIX` can also be a list of strings, in which case any
suffix from the list will be replaced.  This is the type of target
rule you can see in the example and is suitable for `one-to-one`
builders.  Another use of this rule type could be seen in
byte-compiling builder:
  •   :targets        (".el" -> ".elc")
    

And the most powerful of all target rules: a function (can be a lambda form or a function name). It is called with a list of sources (even if the builder is of one-to-one or one-to-many type) and must return one of the types enumerated above.

[#collecting-virtual-targets] ==== Collecting into virtual targets

Real targets generated by the builders can optionally be combined into virtual targets. The latter are used to easily build all real targets of the same type; some (:default, :compile etc.) also have special meaning to certain commands.

Like with the target rules, there are several ways to collect the targets.

VIRTUAL-TARGET::

All real targets generated by the builder are combined into given
`VIRTUAL-TARGET`.  This is what you can see in the example.

(VIRTUAL-TARGET-1 [VIRTUAL-TARGET-2 [\... VIRTUAL-TARGET-N]])::

Combine the real targets into `VIRTUAL-TARGET-N`, then put it to
the preceding virtual target and so on.  This format is currently
unused in standard Eldev builders.  It can generate target trees
of this form:

-- :gen-files :gen-sources :gen-el foo.el.in bar.el.in

It is expected (even if not required) that a different builder adds another branch to the tree, actually making it useful.

(ENTRY...), each ENTRY being (REAL-TARGETS VIRTUAL-TARGETS)::

Both of `REAL-TARGETS` and `VIRTUAL-TARGETS` must be either a list
or a single target string.  For each `ENTRY` this repeats the
logic of one of the two formats above, but instead of all targets
for the builder uses only those listed in `REAL-TARGETS` for the
`ENTRY`.  This is not often needed, but can be useful if builder’s
targets come in two or more substantially different kinds.

Like with <<target-rules,target rules>>, you can specify a function here. Such a function gets called with a list of real targets and must return a collection rule in one of the formats listed above.

==== Summary

To define a builder you need to write an Elisp function that generates target(s) from source(s). If it processes multiple sources at once or generates multiple targets, give it the appropriate :type. Write a fileset that matches its :source-files and a rule to determine target names from those — parameter :targets. If you want the targets grouped together into virtual target(s), add :collect keyword. You should probably also add a :define-cleaner that removes generated targets.

Parameters :name, :short-name, :message and :briefdoc are all fully presentational and thus not very important. But if you want to write a nice and polished builder, investigate them too.

=== Adding commands and options

Eldev has lots of standard commands, but sometimes you need to define yet more. Commands should generally be defined for things that cannot be reformulated in terms of building targets. If a command would just create a file, e.g. extract documentation from source code, an additional builder would be more suitable.

Defining a command is not much more complicated than defining a normal Elisp function:

[source]

(eldev-defcommand mypackage-parrot (&rest parameters) "Repeat parameters from the command line." :parameters "TEXT-TO-PARROT" :aliases (copycat ape) (unless parameters (signal 'eldev-wrong-command-usage `(t "Nothing to say"))) (eldev-output "%s" (mapconcat #'identity parameters " ")))

Macro eldev-defcommand works much like defun, but additionally it adds the new function to the list of Eldev command handlers. New command receives name built from the function name by removing package prefix. If that doesn’t produce the needed result in your case (e.g. if package prefix is two words in your project), you can always specify name explicitly by using :command parameter. You can also give your command any number of aliases, as shown above.

Keyword :parameter describes what the command expects to see on the command line. It is used when invoking eldev help COMMAND to improve documentation: all commands are automatically documented. The short one-liner for eldev help is derived from the function’s documentation by taking the first sentence. If this is not good enough in your case, use keyword :briefdoc to set it explicitly.

When command is invoked from command line, Eldev calls the corresponding function, passing all remaining parameters to it as strings. The example command above just parrots the parameters back at user, in accordance to its name.

==== Defining options

You have probably noticed that the command function we’ve defined doesn’t accept any options. In fact, this is true for all commands in Eldev: options are not passed to them. Eldev takes a different approach: whenever a (recognized) option is encountered on the command line, appropriate function is called, which is supposed to alter global state. This way it is easy to share options between multiple commands when needed.

So, with that in mind, let’s expand our example command with an option:

[source]

(defvar mypackage-parrot-colorize-as nil)

(eldev-defcommand mypackage-parrot (&rest parameters) "Repeat parameters from the command line. If you want, I can even colorize them!" :parameters "TEXT-TO-PARROT" :aliases (copycat ape) (unless parameters (signal 'eldev-wrong-command-usage `(t "Nothing to say"))) (let ((text (mapconcat #'identity parameters " "))) (when mypackage-parrot-colorize-as (setf text (eldev-colorize text mypackage-parrot-colorize-as))) (eldev-output "%s" text)))

(eldev-defoption mypackage-parrot-colorize (&optional style) "Apply given STYLE to the parroted text (`section' if not specified)" :options (-c --colorize) :optional-value STYLE :for-command parrot (setf mypackage-parrot-colorize-as (intern (or style "section"))))

Definition of mypackage-parrot is updated, but there is nothing Eldev-specific here. Let’s rather have a look at the option definition.

Unlike for command function, name of option function is not important. Instead, how the option looks like on the command line is determined by :options keyword. It can specify any number of alternatives, but they all must be either short-style (single - followed by one letter) or long-style (-- followed by a longer name) options. Some options take a value; it is determined by parameter :optional-value or :value (if the value is mandatory) and must match arguments in function definition.

Options can be either global or command-specific. In the latter case — the one you’ll typically need — you define to which command(s) the option applies using :for-command parameter. In our case its value is a single command, but it can also be a list of commands.

To test how the new option works, run:

$ eldev parrot -c Repeat this

It should print text “Repeat this” in bold, unless you’ve disabled output colorizing.

Note that the command doesn’t repeat “-c”, even though it appears on the command line. That’s because Eldev doesn’t pass the options as parameters to commands: only non-option arguments remain.

Documentation (i.e. output of eldev help parrot) for the command we defined above now automatically lists the accepted option:

.... Usage: eldev [OPTION...] parrot TEXT-TO-PARROT

Command aliases: copycat, ape

Options: -c, --colorize[=STYLE] Apply given STYLE to the parroted text (‘section’ if not specified)

Repeat parameters from the command line. If you want, I can even colorize them! ....

=== Custom test runners

FIXME

== Influential environment variables

A few environment variables can affect Eldev’s behavior.

EMACS or ELDEV_EMACS::

Use given Emacs executable (also for any child processes).  If not
specified, this defaults to just `emacs`, which is expected
somewhere in `$PATH`.

ELDEV_LOCAL::

Load Eldev Elisp code from given directory (usually a Git clone of
source tree) instead of the normal bootstrapping from MELPA.
Should not be needed normally, only when developing Eldev itself.

ELDEV_DIR::

Directory where user’s configuration file, Eldev’s bootstrapping
files etc. are located, defaults to `~/.eldev`.  Used by Eldev’s
own regression tests, should be of no interest for typical use.

== See also

Other build tools you might want to use instead of Eldev:

  • {uri-cask}[Cask] — the most established Emacs project management tool.
  • {uri-makem-sh}[makem.sh] — a shell script that performs many common Elisp development tasks; must be copied to your project.
  • {uri-emake}[EMake] — build tool that combines Elisp with GNU Make.
  • {uri-keg}[Keg] — a build tool that can be seen as a successor to Cask; uses a similar project description file.
  • {uri-makel}[makel] — a prebuilt Makefile with typical targets useful to Elisp projects.

Projects and services that can otherwise help you with developing your Elisp code:

  • {uri-evm}[EVM] — Emacs version manager; has special support for Travis CI.
  • {uri-nix-emacs}[nix-emacs-ci] — installer of different Emacs versions that uses Nix and Cachix; useful for continuous integration.
  • {uri-github-wflows}[GitHub workflows] — a part of GitHub available to any hosted project, which can be used for <<github-workflows,continuous integration>> among other things.
  • {uri-travis}[Travis CI] — continuous integration service, the most used one for Elisp projects; Eldev has <<travis-ci,additional support>> for it.
  • {uri-circle}[CircleCI] — another continuous integration service; Eldev provides <<circle-ci,a special installation script>> for it.
  • {uri-coveralls}[Coveralls] — web service to help you track your code coverage over time; can be integrated with Eldev using <<undercover-plugin,a plugin>>;
  • {uri-undercover}[undercover] — a tool for generating test coverage reports for Elisp code; also see Coveralls above.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].