Thomas Becker's Free Software Utilities
 
 

Devenv Emulation:
Improved Compiling and Debugging (gcc and gdb Integration) in GNU Emacs 21

Copyright (C) 2007 Thomas Becker

Contents

  1. Overview
  2. Download and Installation
  3. Feedback and Bug Reports
  4. Using Devenv Emulation

Overview

GNU Emacs supports integrated compiling and debugging. This brings with it such conveniences as jumping from a compiler error message directly to the offending location in the source code, and source-level debugging in Emacs buffers. Some people argue that turning an editor into a development environment like that is an ill-fated endeavour from the start; they hold that a development environment should be a stand-alone program of which the editor is an integrated component. Be that as it may, many of us, including myself, use GNU Emacs' compiling and debugging features.

Unfortunately, if you use GNU Emacs out of the box, compiling and debugging come with some annoyances. My personal pet peeves are:

  • In an integrated development environment such as Microsoft Visual Studio, the first thing you do is open a project (or "solution" in Microsoft speak). After that, tasks such as compiling an individual source file, building the whole thing, starting a debug session, etc. can be kicked off with function keys. One never has to to type a compile, make, or debug command anymore. For me, that does make a big difference in productivity.
     
  • Many compilers, including gcc, output error messages where the file location of the error is given as a relative path. That causes trouble in large projects, where source files are scattered over several directories. In that case, jumping from the error messages in the compilation output buffer to the respective locations in the source files via next-error (pressing Ctrl-x `) has a problem: depending on your setup, it can either work when compiling individual source files, or it can work when compiling the entire project, but not for both.

The Devenv Emulation package removes these annoyances and adds some minor conveniences. Among other things, it provides function key bindings that are modeled after Microsoft Visual Studio.

Besides download and installation, all that is required to get the benefit of Devenv Emulation is to give Emacs some information about your current project build environment. Typically, this is done in the Emacs initialization file. See the documentation below for details.

Download and Installation

Devenv Emulation can be downloaded from here. To install Devenv Emulation, place the file devenv-emulation.elc in some directory DIR. Add the line

(load-file "DIR/devenv-emulation.elc")

to your Emacs initialization file, where you have replaced the string "DIR" in the load-file call with the actual directory where devenv-emulation.elc resides. "DIR" may of course be omitted if devenv-emulation.elc is in a directory where Emacs looks for lisp files. Be sure to load devenv-emulation.elc and not devenv-emulation.el. It is customary but not necessary to put the .el file in the same directory as the .elc file.

Feedback and Bug Reports

Click to send feedback and bug reports concerning Devenv Emulation.

Using Devenv Emulation

Overview

To use Devenv Emulation, you must give Emacs some information about your current project build environment. That is the equivalent of opening a project in an integrated development environment. Typically, this is done in the Emacs initialization file. However, all the settings described below can be made or modified at any time during an Emacs session.

In most cases, only two variable settings are required, like this:


(setq devenv-emulation-active-project-name "hello_world")
(setq devenv-emulation-active-compilation-directory "~/HelloWorld/")

Furthermore, you will have to set some key bindings for the most common compile and debug commands. All this is explained further in the sections below.

The Compilation Directory

Remember, one of the main reasons for using Devenv Emulation is to deal with compilers (such as gcc) that give the error loactions in their messages relative to the compilation directory. There is one prerequisite on your build environment for Devenv Emulation to do its job in that situation: the compilation directory, that is, the working directory under which the compiler gets called, must be the same across all targets. It would be very hard to make Emacs' integrated compiling work properly and consistently if the error locations weren't always relative to the same directory. What this means is that you must not use the cd command in your makefile(s), or if you do, then change to the same directory everytime.

You must tell Devenv Emulation about your active compilation directory with a line like


(setq devenv-emulation-active-compilation-directory "~/HelloWorld/")

The directory can be specified in unexpanded form, as in the example above, and it may be given with or without the trailing slash. This variable setting, like all the other ones described below, can also be made interactively with M-x set-variable.

The Build Command

One of the benefits of Devenv Emulation is that you can compile individual source files or make the entire project by hitting a function key, and you can make other targets by calling a Lisp function via a key combination.

For this to work, you must tell Devenv Emulation what your make command is. The simplest way to achieve this is to specify an active project name with a variable setting like this:


(setq devenv-emulation-active-project-name "hello_world")

Devenv Emulation will then perform the following lookup when it is told to make a target:
  1. If the active compilation directory contains a script whose name is the active project name with "make_" prepended to it, then that script will be called with the target name as the only argument. (See Section "Target Names" below for an explanation of where Devenv Emulation gets the target name from.) In our example so far, Devenv Emulation would call
    
    make_hello_world <target_name>
    
    
    with ~/HelloWorld as the current directory.
     
  2. If the script as described in 1. above does not exist, Devenv Emulation assumes that your active compilation directory contains a makefile whose extension is .mak and whose basename is the same as the active project name, and that you want to call the standard make program on this makefile. In our example, Devenv Emulation would call
    
    make -f hello_world.mak <target_name>
    
    
    with ~/HelloWorld as the current directory.
     
If none of these two alternatives is good for you, you can specify a different compile command altogether like this:

(setq devenv-emulation-active-compile-command "gmake -f my_make_file.mak")

If this variable is non-nil, it supersedes the lookup described in 1. and 2. above. Again, the target name will be passed as the only argument, and the working directory for the call will be the active compilation directory.

Target Names

Devenv Emulation provides a function that prompts the user for a target to be made. Furthermore, it has predefined functions to make the entire project, to clean the entire project for a full rebuild, and to compile the source file that is being visited in the current buffer. (See Section "Functions and Key Bindings" below for more details.)

The default target names that Devenv Emulation uses for making and cleaning the entire project are "all" and "clean". You can change these by setting variables as follows:


(setq devenv-emulation-make-all-target "tutto")
(setq devenv-emulation-clean-all-target "klean")

The default target name for compiling the source file that is visited in the current buffer is obtained by appending .o to the basename of the source file. For example, the target name for compiling hello_world.cpp is hello_world.o. You can specify a different extension for the target name as follows:

(setq devenv-emulation-object-file-extension ".obj")

Note that the period is part of the extension that you set. Hence, you can use this to remove the extension of the source file altogether, or to append something to the basename of the file. Similarly, you can append a prefix to the target name:

(setq devenv-emulation-object-file-prefix "release/")

Together, the two settings above will cause the target name for the source file hello_world.cpp to become release/hello_world.obj.

If you need more elaborate rules to turn source file names into the corresponding target names, you can do so by defining the lisp function devenv-emulation-file-name-2-target. If this function is defined, the full, expanded path of the source file will be passed to it, and the return value will be used as the corresponding target name. In the following example, the target name used for each source file will be the full path name of the source file, with the file extension set to "obj":


(defun devenv-emulation-file-name-2-target (file-path)
   (concat (file-name-sans-extension file-path) ".obj"))

Note that you can use this function to make the target names for your source files dependent on some global variable setting. This can be useful, for example, to switch between debug mode and release mode.

The Debug Executable

When it comes to starting a debug session, Devenv Emulation assumes by default that your executable for debugging is in the active compilation directory, and that its name is identical to the active project name. In our example, the debugger would try to debug

~/HelloWorld/hello_world

If this is not good for you, you can specify the full pathname of your executable by assigning it to the variable devenv-emulation-active-executable. Here's an example that modifies the default behavior so that the executable is found in the subdirectory "bin" of the active compilation directory and has the extension "exe":


(setq devenv-emulation-active-executable
      (concat
       devenv-emulation-active-compilation-directory
       "bin/"
       devenv-emulation-active-project-name
       ".exe"
      ))

Switching between Modes or Projects

If you find yourself having to switch frequently between different projects or different modes such as relase mode and debug mode, you will want to have an easy way to switch between different settings for the Devenv Emulation package. Even if you don't know much about Emacs Lisp programming, you can easily achieve this by wrapping each set of settings in a function call. The following is an excerpt from a .emacs file that allows to switch interactively between release mode and debug mode for a project that was created under Windows with the Qt qmake program:

(setq devenv-emulation-active-project-name "QtProject")
(setq devenv-emulation-active-compilation-directory "C:/Development/QtProject")
(setq devenv-emulation-object-file-extension ".obj")
;
(defun devenv-emulation-release-mode ()
  "Switches to release mode"
  (interactive)
  (setq devenv-emulation-active-executable
        (concat
         devenv-emulation-active-compilation-directory
         "/release/"
         devenv-emulation-active-project-name
         ".exe"
         ))
  (setq devenv-emulation-active-compile-command "nmake_release.cmd")
  (setq devenv-emulation-object-file-prefix "release\\")
  )
;
(defun devenv-emulation-debug-mode ()
  "Switches to debug mode"
  (interactive)
  (setq devenv-emulation-active-executable
        (concat
         devenv-emulation-active-compilation-directory
         "/debug/"
         devenv-emulation-active-project-name
         ".exe"
         ))
  (setq devenv-emulation-active-compile-command "nmake_debug.cmd")
  (setq devenv-emulation-object-file-prefix "debug\\")
  )

One can now interactively switch between debug mode and release mode, like this:
M-x devenv-emulation-release-mode

M-x devenv-emulation-debug-mode

Key Bindings

Devenv Emulation is not worth a whole lot unless you make some key bindings. The suggested default bindings are collected in the function devenv-emulation-set-key-bindings. It is recommended that you make these key bindings globally, by calling

(devenv-emulation-set-key-bindings global-map)

The key bindings are described in more detail in the next section.

Given the abundance of GNU Emacs packages and modes, it may happen that some package's or mode's key bindings conflict with Devenv Emulation's bindings. In that case, you have two options. For one thing, you can easily modify the key bindings to your preference by copying, modifying, and renaming the Emacs Lisp function devenv-emulation-set-key-bindings out of the source file devenv-emulation.el.

Alternately, you can override the offending package's or mode's key bindings. To override other packages' key bindings, make the call to devenv-emulation-set-key-bindings last in you Emacs initialization file. To trump a mode's key bindings, you need to call devenv-emulation-set-key-bindings on the current local map as the last function in that mode's hook, like this:


(add-hook 'joes-mode-hook
          '(lambda ()
             (devenv-emulation-set-key-bindings (current-local-map))))

If Joe's mode does not have a hook, then it's a bad mode and you should not be using it.

Functions and Key Bindings in Detail

For details on how the make command, the target names, and the path name of the debug executable are obtained, see Sections "The Build Command", "Target Names", and "The Debug Executable" above.
Making and Compiling

F7   Build the active project.
Shift-F7   Clean the active project for a full rebuild.
Ctrl-F7   Compile the file that the active buffer is visiting.
Ctrl-C m   Make a specific target. Prompts for a target name. The default is the previously chosen target. Minibuffer history is kept specifically for target names. If you want minibuffer completion to be available, you must tell Devenv Emulation what your targets are. This is done with a function call like this:
(devenv-emulation-add-target-names-for-completion
    "netlib" "guilib" "dblib" "mathlib")
F4   Go to the next compilation error message and the corresponding source code (Emacs function next-error).
Shift-F4   Go to the previous compilation error message and the corresponding source code (Emacs function previous-error).

Debugging

F5   Start debugging. If there is no active debug session, then gdb is called on the active executable, and the GUD buffer is displayed. The active executable is not started, so you get a chance to set breakpoints. Pressing F5 again will then start the active executable under the debugger, that is, it will pass the command "run" to gdb.
Shift-F5   Kill the active executable if it is being debugged, that is, pass the command "kill" to gdb.
F9   Set a breakpoint at the source line that the point is on (Emacs function gud-break).
Shift-F9   Delete the breakpoint(s) on the current source line (Emacs function gud-remove).
Ctrl-F9   Set a temporary breakpoint on the current source line (Emacs function gud-tbreak).
F10   Execute current source line (Emacs function gud-next).
Shift-F10   Continue execution (Emacs function gud-cont).
F11   Execute until another source line is reached (Emacs function gud-step).
Shift-F11   Run until the selected stack frame returns (Emacs function gud-finish).
F12   Move down the stack frames (Emacs function gud-down).
Shift-F12   Move up the stack frames (Emacs function gud-up).

Miscellaneous
Ctrl-C c   Switch to buffer *compilation* if it exists. If a GUD buffer is visible, then that window is used. If not, the compilation buffer is shown in a new window that is displayed in the same manner as when a compilation is first started.
Ctrl-C d   Switch to the GUD buffer if it exists. If a compilation buffer is visible, then that window is used. If not, the GUD buffer is shown in a new window that is displayed in the same manner as when a debug session is first started.

Back to top