Autobuild is a framework for defining and executing arbitrary build rules.
Autobuild rules contain knowledge about how to carry out a certain type of build under specific contexts.
When an autobuild rule is applicable under the current context in emacs, it generates an action which details how to carry out the build.
Autobuild rule actions may range from simple compile-command strings, to emacs-lisp functions, to pipelines that chain multiple build rules together to create more complete build processes, with individual steps that may be either synchronous or asynchronous.
Autobuild rules are zero-ary named functions that contain knowledge about what “build” means under a specific context. A context may involve the current buffer’s major-mode, the name of the current file, the presence of other files in the current directory (for example, “Makefile” or “BUILD”), or any other emacs lisp predicate.
Because most build rules are limited to certain major modes, all rules must
specify a list mode-filter
, which, if non-nil,
restricts the applicability of the rule to the major modes listed.
When a rule generates a non-nil action, it communicates that it knows how to “build” in the current context, and specifies how to do so as part of the action.
An action may be either a string, which is executed with compile
, or a zero-ary function.
(autobuild-define-rule autobuild-el-eval-buffer (emacs-lisp-mode)
"Evaluate the current emacs-lisp buffer"
#'eval-buffer)
(autobuild-define-rule autobuild-cl-slime-eval (lisp-mode)
"Evaluate the current lisp buffer"
#'slime-compile-and-load-file)
(autobuild-define-rule autobuild-html-browse (html-mode mhtml-mode)
"Open the current html file in the browser"
(let ((url (format "file://%s" (buffer-file-name))))
(apply-partially #'browse-url url)))
(autobuild-define-rule autobuild-run-executable nil
"Run the current buffer's underlying file as an executable if possible."
(autobuild-nice 9)
(let ((filename (buffer-file-name)))
(when (and filename
(file-executable-p filename))
(format "./%s" (f-filename filename)))))
(autobuild-define-rule autobuild-shell-script-syntax-check (sh-mode)
"Syntax-check a bash script without running it"
(autobuild-nice 15)
(let ((fn (f-filename (buffer-file-name))))
(format "bash -n %s" fn)))
(autobuild-define-rule autobuild-file-local-compile-command-set nil
"Set and run the file-local compile command"
(lambda ()
(autobuild-nice 12)
(let ((command
(read-shell-command "enter compile command: "
(alist-get 'compile-command file-local-variables-alist))))
(add-file-local-variable 'compile-command command)
(setq compile-command command)
(compile compile-command))))
(autobuild-define-rule autobuild-ispell (text-mode)
"Do a spell check"
#'ispell)
This is the main user-facing command entry-point, and should be bound to a key.
autobuild-build
obtains a list of the currently-applicable build rule actions and
executes the one with highest priority.
With a prefix argument, the user is prompted to explicitly select a build rule or to change the current one.
autobuild-build
remembers the last rule executed at the buffer-local level, and
re-executes it if it is known and if prompting was not requested.
A rule may specify the priority of its action by setting the value of
autobuild-nice
, with lower nice values denoting higher priority.
autobuild-build
defaults to selecting the action with the highest priority, unless
prompting was requested via a prefix argument.
The most specific rule under a certain context should strive to have the highest priority
in order for autobuild-build
to select it as the default.
Several build rules may be chained together to form compilation-pipelines. (TODO example)
Within a pipeline, if a rule’s action is either a string, which denotes a compile-command,
or a function whose value is a compilation buffer, the action is considered
to be an asynchronous compile
command, and any remaining steps in a compilation pipeline
are only resumed after the current command has succeeded.
This function is invoked when an asynchronous compilation command or pipeline has completed.
It is subject to autobuild-notify-threshold-secs
. It may be used to notify the user
in custom ways, for example via notify-send “desktop” notifications, beeps, etc.
- Add melpa as a package repository
(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
M-x package-refresh-contents
M-x package-install RET autobuild
- Add autobuild to the load path and require it, or simply load the file.
(add-to-list 'load-path (expand-file-name "~/git/autobuild/")) (add-to-list 'load-path (expand-file-name "~/git/autobuild/common-rules")) (require 'autobuild) (require 'autobuild-common-rules)
- Enable global autobuild mode
- Bind
#'autobuild-build
to a key. - Define appropriate rules.
- Optionally load
autobuild-examples
for a some commonly-applicable build rules.
- Optionally load
- Invoke
autobuild-build
to build in the current context.
(autobuild-mode)
(global-set-key (kbd "M-c") #'autobuild-build)
Autobuild is different from other similar packages in that it provides a menu of various applicable build options, from which the user makes an explicit or implicit choice, as opposed to a providing a single line of hooks that are always implicitly executed. This ability to make an explicit choice about which build rule to apply allows users to switch back-and-forth between several different flows for whatever building/compiling means in the current context.
Autobuild rules are also capable of dynamically determining their own applicability, priority, and are able to customize generated build actions based on the current contex.