If You Build It…
The last few weeks I've been playing with the Raspberry Pi. A friend of mine was watching my latest demo and laughed at me because I wasn’t using a "modern" IDE. I've mentioned before I'm totally tied to emacs, and while I use Eclipse and a few other IDEs from time to time, given the choice, I always just go back to emacs.
However, that's not such a bad thing. Not that I have anything against an IDE. Adafruit's webIDE looks nice. I occasionally work through Eclipse and NetBeans when I need tools that are built into it. Cloud9 looks great but I can never get it to run correctly on the BeagleBone or on the Pi. But I can also do a surprising amount of workflow automation with emacs, the shell, and a few handy scripts.
Like most of what I've been talking about with the Pi, these scripts aren't really specific to any piece of hardware. That's one of the most fun things about the Pi: It runs pretty ordinary Linux so all the tools I've been building up over the years work.
The listing below shows a builder script that I've used for awhile (download the builder file here). It does require you to install inotify-tools on the Pi (apt-get install inotify-tools will do the trick). It also assumes you have the basic development tools like
#!/bin/bash if [ "$1" == "-h" -o "$1" == "--help" ] then echo 1>&2 "Builder by Al Williams, Dr. Dobb's http://www.drdobbs.com/embedded" echo 1>&2 Usage: Builder [directory [make_tag [log_file]]] echo 1>&2 Directory defaults to . log defaults to ./builder.log echo 1>&2 Note to use a different Makefile use -ffile_name as tag echo 1>&2 with no space between -f and the file name exit 1 fi # parse arguments if [ "$1" == "-k" ] then if [ "$2" != "" ] then cd "$2" fi echo Killing builder in $PWD touch .builder-kill exit 0 fi BDIR="$1" if [ "$BDIR" == "" ] then BDIR="." fi cd "$BDIR" # delete kill file rm -f .builder-kill CMD="make" if [ "$2" != "" ] then CMD="make $2" fi LOG="$3" if [ "$LOG" == "" ] then LOG="$BDIR/builder.log" fi # create log pipe if nothing there # could use touch if you want the log to be persistent # touch "$LOG" # or named pipe if you don't mknod "$LOG" p while true do # Check make to see if a build is needed if $CMD -q 2>>"$LOG" then # nothing to do true else # run make $CMD >>"$LOG" 2>&1 fi # wait for a change (note always build the first run) # use this line for lots of chatter in the log # inotifywait -e close_write,move,create,delete "$BDIR" >>"$LOG" 2>/dev/null # or this line to quiet it inotifywait -e close_write,move,create,delete "$BDIR" 2>/dev/null >/dev/null if [ -f ".builder-kill" ] then # comment this out if you want the log to persist # rm -f "$LOG" exit 0 fi done
The script is pretty simple. When it runs, it does a little housekeeping. Then it runs
make with the
–q option. This causes
make to look and see if anything needs to be done to build the specified target (or the default target, usually). It doesn't take any action, but it does return a 0 if the project is up to date and 1 if it isn't.
The script can test for this and then actually runs
make if the project is out of date. In theory, you could just run this over and over, but that doesn't seem very efficient. Instead,
inotifywait watches the build directory for changes. This is a shell command that efficiently returns information about changes in the filesystem. You can register for what events you are interested in, but you have to be careful. If you haven't used
inotifywait before, you might assume that
close_write is all you need to detect an edit in the project. However, most editors don't ever directly close a changed file. Instead, they edit a backup copy, save it, and then move it to the original file. This ensures they can always preserve the original file if there is an error or the user abandons the file.
The trick is to watch for several events, including
move. Ordinarily, you'd examine the output of the command to see if a file you were interested in actually changed. But with
make, it is easier to use
–q and let it do all the work.
The output of the commands can be sent to a file or a named pipe. A tail
–f will show the log as it generates messages.
There are several ways I can use this with emacs. The simplest way is to run emacs on the Pi. Open a shell window and run the builder script there in the background. Then a tail
–f on the log output in the same window gives status on the build process. Other emacs windows can edit the files and when you make a change you instantly see the build results (which, of course, may be an error if you aren't done with changes yet).
Usually, though, I use sshfs to mount the Pi as part of my filesystem. Then I run emacs locally and my builder window will use ssh to run both builder and the tail command. This allows me to use my "native" emacs with all of its customizations (and speed). If you prefer, you could use emacs remote files (provided by tramp) to get the same effect.
When you are done with the builder script, you can just kill it. If you prefer, you can run it again for the same directory with the
–k option. This will write a special file that signals any scripts in that directory to exit.
One nice thing about this script is it really isn't tied to emacs at all. If you have a way to edit files on the Pi and a way to look at the output from a remote file or pipe, you are in business. The script is easy enough to modify for special purposes, and gives immediate feedback on things like syntax errors as you are saving your source files.
Of course, last time I talked about building Pi programs on the remote host. That's an easy modification too, and I'll talk about that next time.