Saturday, September 12, 2009

My Clojure Setup

Update: There is now an official Getting Started page for Clojure. Please refer to the instructions on this page for information about how to get started with Clojure in the environment that you are most comfortable with. The information below may now be irrelevant.

I have just switched over to using SLIME instead of the basic Lisp interaction that was provided with clojure-mode. A discussion on the clojure-group prompted me to do this. In this same discussion there where questions about how people setup Clojure and how it can be difficult for new users to get started. My setup is not perfect but I am really starting to like it. I thought I would share with others and maybe get some feedback on improvements I can make. I will update this entry as I make changes to my setup.

Overview

Let me tell you a little bit about my setup so that you can get into how cool it is and then get excited about reading all of the stuff below.

I have a command on my system named clj.

clj => starts a REPL
clj some_file.clj => Runs the script some_file.clj
clj some_file.clj arg1 arg2 => Runs the script some_file.clj and passes 
arg1 arg2 to the script

When I start to get serious, I create a "project" which looks like this:

my-project
.cljrc.clj
bin
lib
src
test

The file .cljrc.clj establishes this as a Clojure project. If I execute clj anywhere within this project it will determine the root of the project by locating the .cljrc.clj file and then start the REPL in that directory. It will also load any jar files within the lib directory into the classpath. The file .cljrc.clj is passed as a parameter to the REPL which will execute the Clojure code contained therein.

bin contains commands that allow me to do things like run my tests, compile java code, package java code in a jar file and put it in the lib directory for use from Clojure, etc...

All source files go into src and all tests go into test.

I can run SLIME from emacs and the clj script will be used to start Clojure. Once I have started a REPL using this command. I can open emacs and connect to the running REPL and interact with it from within emacs.

Configure Emacs

I did not use M-x clojure-install to configure emacs. Instead, I followed this tutorial http://riddell.us/tutorial/slime_swank/slime_swank.html which worked for me on OS X even though it targets Ubuntu. After doing this, I had SLIME working with Clojure in emacs.

Here is my .emacs file:

;; clojure-mode
(add-to-list 'load-path "/opt/clojure/clojure-mode")
(require 'clojure-mode)

;; swank-clojure
(add-to-list 'load-path "/opt/clojure/swank-clojure")
(setq swank-clojure-binary "clj")
(require 'swank-clojure-autoload)

;; slime
(eval-after-load "slime"
`(progn (slime-setup '(slime-repl))))

(add-to-list 'load-path "/opt/slime")
(require 'slime)
(slime-setup)

Notice that I added the swank-clojure-binary line instead of swank-clojure-config.

The Ruby Script

This is the Ruby script that I use to start Clojure. I don't do much Ruby coding so please don't laugh.

#!/usr/bin/env ruby -wKU

JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home"
JAVA="#{JAVA_HOME}/bin/java"
GIT_ROOT=ENV['GIT_ROOT']
CLJ="/opt/clojure"
LIB="#{GIT_ROOT}/library"
classpath=".:src:test:classes" +
":#{CLJ}/clojure/clojure.jar" +
":#{CLJ}/clojure-contrib/clojure-contrib.jar" +
":#{CLJ}/swank-clojure" +
":#{LIB}/tools/ant.jar" +
":#{LIB}/tools/ant-launcher.jar" +
":#{GIT_ROOT}/research/clojure/fpl-clojure-util/fpl-clojure-util.jar"
JLINE="#{LIB}/bin-tools/jline-0.9.94.jar"

def find_root(d, repl_config)
Dir.chdir(d)
if Dir.pwd == "/"
nil
else
if Dir.glob("#{repl_config}").size == 1
Dir.pwd
else
find_root("../", repl_config)
end
end
end

repl_config=".cljrc.clj"
dir = Dir.pwd
if find_root(dir, repl_config)
puts "Running in #{Dir.pwd}"
else
Dir.chdir(dir)
puts "Running in #{Dir.pwd}"
puts "Using default #{repl_config}"
repl_config="~/.cljrc.clj"
end

if File.directory? "lib"
Dir.foreach("lib") do |x|
if x =~ /.*.jar$/
classpath = classpath + ":lib/#{x}"
end
end
end

if ARGV[0]
puts "Running Clojure Script... #{ARGV[0]}"
if ARGV[0].size > 1
system("#{JAVA} -Xms500M -Xmx500M -cp #{classpath} "+
"clojure.lang.Script #{ARGV.shift} -- #{ARGV.join(" ")}")
else
system("#{JAVA} -Xms500M -Xmx500M -cp #{classpath} "+
"clojure.lang.Script #{ARGV[0]}")
end
else
puts "Starting Clojure REPL..."
system("#{JAVA} -cp #{classpath}:#{JLINE} jline.ConsoleRunner "+
"clojure.lang.Repl #{repl_config}")
end

Note: This file has been updated in a more recent blog post.

I name this file clj and put it somewhere in my path. If you want to use this then you will need to modify the default libraries that are loaded.

Here is what I have in .cljrc.clj:

(use '[clojure.contrib.duck-streams :only (spit)])
(use '[swank.swank :only (start-server ignore-protocol-version)])

(defn exit [] (. System exit 0))

(defn swank-server []
(ignore-protocol-version "2009-09-08")
(spit (java.io.File. "port.txt") "")
(start-server "port.txt" :port 4005 :dont-close true))


This code will be executed when you run clj within a project. It defines an exit function that you can use to exit the REPL. It also defines a function that will start a swank server from within the REPL. You will need to the set the correct value for ignore-protocol-version. If you don't know what to put here then you can delete this line and emacs will give you a hint as to what it should be.

So how does this all work?

On Mac OS X I have associated the .clj file type with Carbon Emacs. This allows me to open a file for editing by typing open some_file.clj. I can also use wildcards to open multiple files from the command line in emacs.

Once emacs is open, start SLIME by typing M-x slime. This will start the Clojure REPL using the clj script with the classpath just how you want it.

You may also connect to an existing REPL. Start the REPL from the command line using clj. Once the REPL is up type (swank-server). In emacs, type M-x slime-connect. Emacs will ask about the host and port, press enter twice.

That's how I do it for now. I am still working on it. Once I get more comfortable using SLIME I will update this post with any improvements I have made. The one improvement that I do want to make is to change my method of testing to fit with clojure-test-mode so that I can utilize its featues.

No comments:

Post a Comment