My flymake + maven setup

In my day job, we use Maven2 to build our projects and I could not get used to netbeans or eclipse after years of perl and php in emacs (I’m sorry, but no matter how good the refactoring & debugging tools, their text editors are difficult to use and constantly getting in the way, and I really did try). So, I needed a way to run maven from emacs + flymake. I’d seen a few people document their setup, but this is how I got it working. It’s a bit hacky, and non-emacsy, cos it was easier to get going in shell than lisp. Clearly I am an emacs noob.

Since getting this up and running, a friend has pointed out jde-mvn which at some point I will try seriously and maybe even switch to … but until then – hey … I have my dodgy shell scripts.

So, the howto:

in your .emacs:

(require 'flymake-mvn)
(add-hook 'java-mode-hook 'flymake-java-mvn-mode-hook)

in your library-path, create a file (something like ~/emacs.d/flymake-maven.el):

(defun flymake-java-mvn-init ()
  "Return external compile process to run"
  (if jde-global-classpath
	  (list "flymake-maven" (list buffer-file-name (mapconcat 'identity jde-global-classpath ":")))
	(list "flymake-maven" (list buffer-file-name))))

(defun flymake-java-mvn-load ()
  "Setup keys and flymake error patterns"
  (push '(".+\\.java$" flymake-java-mvn-init) flymake-allowed-file-name-masks)
  (push '("^\\([^: \n]+\\):\\[\\([0-9]+\\),\\([0-9]+\\)] \\(.+\n?[^/]*?\n?[^/]*?\\)" 1 2 3 4) flymake-err-line-patterns)
  (push '("^warning \\([^: \n]+\\):\\[\\([0-9]+\\),\\([0-9]+\\)] \\(.+\\)" 1 2 3 4) flymake-err-line-patterns)
  (flymake-mode t)
  (local-set-key (kbd "C-c e") 'flymake-display-err-menu-for-current-line)
  (local-set-key (kbd "C-c n") 'flymake-goto-next-error)
  (local-set-key (kbd "C-c p") 'flymake-goto-prev-error)
  (local-set-key (kbd "C-c s") 'flymake-start-syntax-check)
  (local-set-key (kbd "C-c S") 'flymake-stop-all-syntax-checks)
  (local-set-key (kbd "C-c C-t") 'ed/java-mvn-run-unit-test)
  (local-set-key (kbd "C-c d") 'ed/java-mvn-debug-unit-test)
  (local-set-key (kbd "C-c b") 'jde-bug-set-breakpoint)
  (local-set-key (kbd "C-c c") 'jde-bug-clear-breakpoint)
  (local-set-key (kbd "C-c C-i") 'ed/java-indent-properties)
  (local-set-key (kbd "C-c C-.") 'jde-complete)
  (local-set-key (kbd "C-c C-/") 'semantic-complete-analyze-and-replace)
  (local-set-key (kbd "C-c C-m") 'tempo-snippets-clear-all))

(defun flymake-java-mvn-mode-hook ()
  "Initialise the java environment"
  (java-mode-indent-annotations-setup)
  (c-subword-mode 1)
  (flymake-java-mvn-load)
  (flyspell-prog-mode)
  (setq indent-tabs-mode nil)
  (setq local-abbrev-table java-mode-abbrev-table)
  (abbrev-mode 1))

(provide flymake-mvn)

in your shell path (e.g. ~/bin/flymake-maven), create a file:

#!/bin/bash

# find the pom for the specified file
if [ -f $1 ] ; then
	cd `echo $1 | sed 's/[^/]*$//'`
else
	if [ -d $1 ] ; then
		cd $1
	fi
fi
while [ ! -f "pom.xml" ] ; do
	cd ..
	if [ "x/" = "x"`pwd -P` ] ; then
		echo "No pom.xml found"
		exit
	fi
done

# compile the class
if [ "x" != "x$2" -a -d target/classes -o -d build/classes ] ; then
	file=`echo $1 | sed -e 's?/?\\\\/?g' -e 's/\./\\./g'`
	awk_script='BEGIN { line = "" } { if ( $0 ~ /^ *\^/ ) { print line ; line = "" } else if ( $0 ~ /^'$file'/ ) { print line ; line = $0 } else { line = line " ; " $0 } } ; '
	javac -Xlint -classpath target/classes:target/test-classes:build/classes:build/test-classes:$2 -d target/classes $1 2>&1 | awk "$awk_script" | sed 's/  */ /g'
else
	mvn -fn -o -Dmaven.compiler.showWarnings=true compile test-compile | grep -v '\[INFO\]' | perl -p -e 's/(.)\n/$1 ; /gs' | perl -p -e 's/ +/ /g' | perl -p -e 's/\[WARNING\]/warning/' | sort -u
fi

exit 0;

It takes an input of the file you’re editing, and works up the filesystem until it finds a ‘pom.xml’. It decides to call mvn or javac based on the existence of a target/classes or build/classes directory since maven will create the correct directory, and if it all goes wrong I can mvn clean to get back to a known good state. Then it takes the output from maven or javac, ignores all the info logging lines from maven, leaving (hopefully) lots of lovely compile errors in the file we were editing in one line per error, just as flymake expects.

For some reason, I haven’t reliably got maven to report warnings, but javac does that fine, so I just wait for the second compile, or hit C-c s to start flymake off to find out how many unchecked casts I have in todays code.

I Use C-c e to display the error message, C-c n to jump to the next error, and C-c p to get to the previous message.

I have keyboard shortcuts to run the unit tests, and at some point will make that commit to svn when they pass, perhaps with the trac ticket number I’m currently working on from org-mode as a comment … for another day, I feel ;)

The mvn-jde solution looks nicer, and as soon as I can prove that it works for all my day job projects, and I find the time to set it up, I’ll probably switch to that. But I bet maven3 comes out first ;)

0 Responses to “My flymake + maven setup”



  1. No Comments Yet

Leave a Reply




Twitter Updates