The Mastering Emacs ebook cover art

Emacs 28 Edition is out now!

Read a Free Sample

Learn More

Compiling and running scripts in Emacs

How to use Emacs's compile command to run arbitrary programs and scripts, such as unit tests, interpreters or programs.

I’ve talked about running shells and executing shell commands in Emacs before, but that’s mostly used for ad hoc commands or spelunking; what if you want to compile or run your scripts, code or unit tests?

There’s a command for that…

Not surprisingly, Emacs has its own compilation feature, complete with an error message parser that adds syntax highlighting and “go to next/prev error” so you can walk up and down a traceback in Python, or jump to a syntax error, warning or hint in GCC.

There are two ways of using the compilation feature in Emacs: the easiest is invoking M-x compile followed by the compile command you want Emacs to run. So, for Python, you can type M-x compile RET python RET and a new *compilation* buffer will appear with the output of the command. Emacs will parse the output and look for certain patterns – stored in the variables compilation-error-regexp-alist[-alist] – and then highlight the output in the compilation buffer with “hyperlinks” that jump to the file and line (and column, if the tool outputs it) where the error occurred.

If compilation is successful – and like most things in the UNIX world, this is governed by the exit code – the modeline and compilation buffer will say so; likewise, if an error occurred, this is also displayed.

You can jump to the next or previous error ([next/previous]-error) with M-g M-n and M-g M-p – they’re bound to more keys, but I think those are the easiest ones to use. Similarly, in the compilation buffer itself (only), you can go to the next/previous file (compilation-[next/previous]-file) with M-g M-} and M-g M-{, respectively, and RET jumps to the location of the error point is on.

There is an undocumented convention in Emacs that commands like dired, grep, and compile can be rerun, reverted or redisplayed by typing g in the buffer. You can also type M-x recompile to rerun the same compile invocation again.

By default the compilation buffer is just a dumb display and you cannot communicate with the background process. If you pass the universal argument (C-u) you can; the buffer is switched to comint mode, and you can converse with the process as though you were running it in the shell.

Python Debugging with Compile

I work mostly in Python, and quite often I have to debug the script I am writing with pdb, the Python debugger. I use breakpoints a lot, and I have a command bound to F5 that inserts breakpoint(). What it also does is highlight the line in bright red so I don’t miss it.

This is the code I use. Feel free to adapt it for other languages. Send me an e-mail if you do something neat with it.

(require 'python)
(defun python--add-debug-highlight ()
  "Adds a highlighter for use by `python--pdb-breakpoint-string'"
  (highlight-lines-matching-regexp "## DEBUG ##\\s-*$" 'hi-red-b))

(add-hook 'python-mode-hook #'python--add-debug-highlight)

(defvar python--pdb-breakpoint-string "breakpoint() ## DEBUG ##"
  "Python breakpoint string used by `python-insert-breakpoint'")

(defun python-insert-breakpoint ()
  "Inserts a python breakpoint using `pdb'"
  ;; this preserves the correct indentation in case the line above
  ;; point is a nested block
  (insert python--pdb-breakpoint-string))
(define-key python-mode-map (kbd "<f5>") 'python-insert-breakpoint)

There’s a Minor Mode for that…

The compile workflow isn’t for everyone; some people want everything in one place: their shell. Well, good news then – you can have your cake and eat it. The minor mode M-x compilation-shell-minor-mode is designed for comint buffers of all sizes and works well with M-x shell and most modes that use comint. You get the same benefits offered by compile without altering your workflow. If you compile or run interpreted stuff in your Emacs shell you’ll feel like a modern-day Prometheus with this minor mode enabled!

Add this to your init file and the compilation minor mode will start when shell does.

(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)

Further Reading

Have you read my Reading Guide yet? It's a curated guide to most of my articles and I guarantee you'll learn something whether you're a beginner or an expert. And why not check out my book?

Subscribe to the Mastering Emacs newsletter

I write infrequently, so go on — sign up and receive an e-mail when I write new articles