configurable bash script to launch Clojure available

48 views
Skip to first unread message

Stephen C. Gilardi

unread,
Jan 15, 2009, 8:58:58 AM1/15/09
to clo...@googlegroups.com
I've checked in a bash script for launching Clojure to clojure-contrib/
launchers/bash/clj-env-dir.

It's configured using environment variables (one required and several
optional). It sets up the CLASSPATH for the Clojure instance it
launches based on the contents of a directory. This is a mechanism
similar to Java's java.ext.dirs facility but implemented without using
java.ext.dirs. I've tested this on Mac OS X Leopard and Ubuntu Intrepid.

Here's a description of the environment variables it uses:

# Environment variables:
#
# Required:
#
# CLOJURE_EXT The path to a directory containing (either directly or
as
# symbolic links) jar files and/or directories whose paths
# should be included in CLASSPATH. These paths will be
# prepended to any prior definition of the CLASSPATH
# environment variable.
#
# Optional:
#
# CLOJURE_JAVA The command to launch a JVM instance for Clojure
# default: java
# example: /usr/local/bin/java6
#
# CLOJURE_OPTS Java options for this JVM instance
# default:
# example:"-Xms32M -Xmx128M -server"
#
# CLOJURE_MAIN The Java class to launch
# default: clojure.main
# example: clojure.contrib.repl_ln

clj-env-dir can be used directly to launch Clojure or as a building
block for creating launch scripts for a variety of purposes.

Here are two examples of using it as a building block: (these also use
CLOJURE_CONTRIB whose value should be the path to an svn download
directory for clojure-contrib)

"clj" (stock clojure.main call, can launch a repl, script, or just
eval based on arguments to clj)

#!/bin/bash

export CLOJURE_MAIN=clojure.main
CLJ=$CLOJURE_CONTRIB/launchers/bash/clj-env-dir
OPTS=

exec $CLJ $OPTS "$@"

"cljr" (repl using clojure.contrib.repl_ln)

#!/bin/bash

export CLOJURE_MAIN=clojure.contrib.repl_ln
CLJ=$CLOJURE_CONTRIB/launchers/bash/clj-env-dir
OPTS="--init @repl_init.clj --repl"

exec $CLJ $OPTS "$@"

--Steve

Chouser

unread,
Jan 15, 2009, 11:31:21 AM1/15/09
to clo...@googlegroups.com
On Thu, Jan 15, 2009 at 8:58 AM, Stephen C. Gilardi <sque...@mac.com> wrote:
> I've checked in a bash script for launching Clojure to
> clojure-contrib/launchers/bash/clj-env-dir.

I had a launch script (which I've now lost due to my own clumsiness)
that defaulted to a repl if given no file options, and always loaded a
.clojurerc.clj file before starting a repl (whether it was by default
or specifically asked via -r). This allowed me to load repl-utils and
get *print-length* set up before getting a prompt. But if a file was
named with now -r, it was run with no repl. It used appropriate
combinations of clojure.lang.Repl or clojure.lang.Script and file
paths to make this happen.

As far as I can tell this is impossible with clojure.main, which
appears to only allow loading a file *or* starting a repl. Am I
missing a method to support this, or should I go back to using
Script/Repl?

My script also accepted a -cp argument to augment any automatic or
default classpath it set up. I found this useful when tracking down
gen-class issues, but I never had this nice symlink-dir setup, which
might very well be better.

I apologize for not speaking up earlier in clojure.main's development,
when such issues probably could have been addressed more easily.

--Chouser

Konrad Hinsen

unread,
Jan 15, 2009, 11:45:27 AM1/15/09
to clo...@googlegroups.com
On Jan 15, 2009, at 17:31, Chouser wrote:

> I had a launch script (which I've now lost due to my own clumsiness)
> that defaulted to a repl if given no file options, and always loaded a
> .clojurerc.clj file before starting a repl (whether it was by default
> or specifically asked via -r). This allowed me to load repl-utils and
> get *print-length* set up before getting a prompt. But if a file was
> named with now -r, it was run with no repl. It used appropriate
> combinations of clojure.lang.Repl or clojure.lang.Script and file
> paths to make this happen.
>
> As far as I can tell this is impossible with clojure.main, which
> appears to only allow loading a file *or* starting a repl. Am I
> missing a method to support this, or should I go back to using
> Script/Repl?

If I understand correctly what you are looking for, it exists. Here
is my standard command line for starting Clojure:

java -cp $HOME/.clojure/clojure.jar:$HOME/.clojure/clojure-
contrib.jar clojure.main -i $HOME/.clojure/repl-init.clj -r

This first executes repl-init.clj (from where I load repl-utils, set
*print-length* etc.) and then starts the repl.

Konrad.

Mark Volkmann

unread,
Jan 15, 2009, 11:48:16 AM1/15/09
to clo...@googlegroups.com
I think a script like this should be included with the Clojure
download. It's seems to me that everyone needs one and currently has
to write their own by copying example script code from the Getting
Started page. Maybe we can't reach a concensus on everything the
script should do, but providing a good, basic starting point would be
useful. The basic features I would want include:

1) If I run "clj", it starts a REPL.
2) If I run "clj file-path", it runs that file as a Clojure script.
3) Some way to modify the classpath used by the script without
modifying the script.

--
R. Mark Volkmann
Object Computing, Inc.

Chouser

unread,
Jan 15, 2009, 12:08:52 PM1/15/09
to clo...@googlegroups.com
On Thu, Jan 15, 2009 at 11:45 AM, Konrad Hinsen
<konrad...@laposte.net> wrote:
>
> If I understand correctly what you are looking for, it exists. Here
> is my standard command line for starting Clojure:
>
> java -cp $HOME/.clojure/clojure.jar:$HOME/.clojure/clojure-
> contrib.jar clojure.main -i $HOME/.clojure/repl-init.clj -r
>
> This first executes repl-init.clj (from where I load repl-utils, set
> *print-length* etc.) and then starts the repl.

You're absolutely right, that works just fine. In fact, it's
essentially what I already have.

But it wasn't working for me, because in my startup .clj I had (left
over from my old script):

(when (= (System/getProperty "repl") "yes")
(set! *print-length* 103)
(use 'clojure.contrib.repl-utils))

Maybe I should always start a repl. Why did I think I sometimes
wanted no repl? :-)

Anyway, thanks for the example.
--Chouser

Stephen C. Gilardi

unread,
Jan 15, 2009, 12:14:28 PM1/15/09
to clo...@googlegroups.com
On Jan 15, 2009, at 11:31 AM, Chouser wrote:

I had a launch script (which I've now lost due to my own clumsiness)
that defaulted to a repl if given no file options, and always loaded a
.clojurerc.clj file before starting a repl (whether it was by default
or specifically asked via -r).  This allowed me to load repl-utils and
get *print-length* set up before getting a prompt.  But if a file was
named with [no] -r, it was run with no repl.  It used appropriate

combinations of clojure.lang.Repl or clojure.lang.Script and file
paths to make this happen.

As far as I can tell this is impossible with clojure.main, which
appears to only allow loading a file *or* starting a repl.  Am I
missing a method to support this, or should I go back to using
Script/Repl?

Given the clj script I posted earlier (which uses clojure.main directly) all of the following are possible. I think one of these covers the case you're asking about. If not, please let me know.

Launch a repl with no command-line-arguments:

clj

Run a script:

clj my-script

Run a script with command line args:

clj my-script 1 2 3

Load an init file from classpath then run a repl:

clj --init @my-init.clj --repl

Load an init file from classpath then run a repl with command line args:

clj --init @my-init.clj --repl 1 2 3

Load an init file from classpath and run a script with command line args:

clj --init @my-init.clj my-script 1 2 3

Load two init files and run a repl with command line args:

clj --init @my-init.clj --init my-script --repl 1 2 3

I recognize that having this documented at http://clojure.org/repl_and_main will be very helpful to everyone. I'm working on that and I'll upload it before the end of this weekend.

--Steve

Phil Hagelberg

unread,
Jan 15, 2009, 1:27:01 PM1/15/09
to clo...@googlegroups.com
"Mark Volkmann" <r.mark....@gmail.com> writes:

> I think a script like this should be included with the Clojure
> download. It's seems to me that everyone needs one and currently has
> to write their own by copying example script code from the Getting
> Started page.

Strongly agree. If 1.0 is released without this it would be a shame.

-Phil

Timothy Pratley

unread,
Jan 16, 2009, 3:06:24 AM1/16/09
to Clojure
+1 (and a windows .bat file for the unwashed)

I strongly suggest that the script name needs to be retained in
*command-line-args*.
ie: clj myscript.clj 1 2
*command-line-args* should be ("myscript.clj" 1 2)
not (1 2) which is the current behavior of your script with the
current clojure.main
$0 or argv[0] is really useful and widely expected to exist.

I suppose this could be overcome by $1 $@, however that is not quite
correct in the case where the user might pass in additional options
clj --some-option myscript.clj 1 2
so I believe clojure.main itself needs modification to be correct.

Is clojure.main deprecating clojure.lang.Script? I suppose that
http://clojure.org/repl_and_main will explain all when revealed :)



lpetit

unread,
Jan 16, 2009, 7:31:03 AM1/16/09
to Clojure
On Jan 16, 9:06 am, Timothy Pratley <timothyprat...@gmail.com> wrote:
> +1 (and a windows .bat file for the unwashed)

+ 1 too.

> I strongly suggest that the script name needs to be retained in
> *command-line-args*.
> ie: clj myscript.clj 1 2

No, please don't, or it would not be coherent with the way java
handles it.

Maybe for scripts a global var such as *script-name* could be set
automatically ?

> *command-line-args* should be ("myscript.clj" 1 2)
> not (1 2) which is the current behavior of your script with the
> current clojure.main
> $0 or argv[0] is really useful and widely expected to exist.
>
> I suppose this could be overcome by $1 $@, however that is not quite
> correct in the case where the user might pass in additional options
> clj --some-option myscript.clj 1 2
> so I believe clojure.main itself needs modification to be correct.
>
> Is clojure.main deprecating clojure.lang.Script? I suppose thathttp://clojure.org/repl_and_mainwill explain all when revealed :)

Stephen C. Gilardi

unread,
Jan 16, 2009, 8:37:48 AM1/16/09
to clo...@googlegroups.com

On Jan 16, 2009, at 7:31 AM, lpetit wrote:

> Maybe for scripts a global var such as *script-name* could be set
> automatically ?

I like this. I've also seen several requests for a way to know when
code is running in a script which could be satisfied by this as well.

This would be an easy change to clojure.main. I can make a patch.

Rich, will you accept this as an issue?

--Steve

Stephen C. Gilardi

unread,
Jan 16, 2009, 11:27:12 AM1/16/09
to clo...@googlegroups.com

On Jan 16, 2009, at 8:37 AM, Stephen C. Gilardi wrote:

> I like this. I've also seen several requests for a way to know when
> code is running in a script which could be satisfied by this as well.

For clarity, here's what I'm proposing based on recent discussion in
this thread:

[1] When clojure.main runs a script, it will set *command-line-script*
to the script name as it appears on the command line.

Example:

java -cp clojure.jar clojure.main -i dir-utils.clj list-
dir.clj :verbose :depth 3

will run with these values set:

*command-line-script* "list-dir.clj"
*command-line-args* (":verbose" ":depth" "3")

Note that dir-utils.clj is an "init file" here (there can be any
number of them, each introduced by a "-i"), while list-dir.clj is the
main script.

[2] When clojure.lang.Script runs a script, the name immediately
before either the end of line or the "--" will be the script name.
Here's the equivalent to above:

java -cp clojure.jar clojure.lang.Script dir-utils.clj list-dir.clj
-- :verbose :depth 3

(same values set):

*command-line-script* "list-dir.clj"
*command-line-args* (":verbose" ":depth" "3")

Note that in this case, files are still loaded in order, but "init"
files are not "marked". list-dir.clj is considered the main script
because it's the last to appear before the "--" which marks the
beginning of args.

[3] *command-line-script* will be nil in other cases: when
clojure.main launches a repl, when using clojure.lang.Repl, or when
clojure.main reads a script from *in*.

Comments welcome.

--Steve

Chouser

unread,
Jan 16, 2009, 11:37:26 AM1/16/09
to clo...@googlegroups.com
On Fri, Jan 16, 2009 at 11:27 AM, Stephen C. Gilardi <sque...@mac.com> wrote:
>
> [1] When clojure.main runs a script, it will set *command-line-script* to
> the script name as it appears on the command line.
>
> Example:
>
> java -cp clojure.jar clojure.main -i dir-utils.clj list-dir.clj
> :verbose :depth 3
>
> will run with these values set:
>
> *command-line-script* "list-dir.clj"
> *command-line-args* (":verbose" ":depth" "3")
>
> Note that dir-utils.clj is an "init file" here (there can be any number of
> them, each introduced by a "-i"), while list-dir.clj is the main script.

I think this is a good idea.

It would also be useful for a .clj file to be able to determine if
itself is the main script or if instead it's being loaded some other
way (-i, load-file, require, etc.).

The proposed *command-line-script* could be used to make a good guess,
but since it's possible that the main script might have the same file
name as some lib, it wouldn't be perfect. I'm not exactly sure how
this ought to work, since the main script may not be a lib or even be
in the classpath. Maybe a dynamically-bound variable
*loading-main-script* that is bound to true when the main script is
loaded and specifically re-bound to false during lib loading?

--Chouser

Stephen C. Gilardi

unread,
Jan 16, 2009, 3:51:42 PM1/16/09
to clo...@googlegroups.com

On Jan 16, 2009, at 11:37 AM, Chouser wrote:

It would also be useful for a .clj file to be able to determine if
itself is the main script or if instead it's being loaded some other
way (-i, load-file, require, etc.).

How about this which is not quite the same, but has nice properties of its own:

It appears that all flavors of loading ultimately bottleneck at Compiler.load(Reader,String,String). We could add a "*load-level*" var with a root binding of 0 that gets incremented on each entry to that Compiler.load method. A *load-level* of 1 would indicate that this file is being loaded from the "top level" with no other load pending.

A script could behave differently on (= *load-level* 1) (or a suitably named function that returns the same boolean value).

I like this because it's simple to implement and explain and it also covers the case of loading a script explicitly from the repl. Arguably a script loaded by the user from the top level at the repl ought to behave as a "main" script as well.

--Steve

Timothy Pratley

unread,
Jan 16, 2009, 7:36:39 PM1/16/09
to Clojure
I guess another way would be to just rebind *command-line-script* to
nil on subsequent loads? It doesn't feel like an complete solution but
I believe it covers the use cases discussed so far.

Stephen C. Gilardi

unread,
Jan 16, 2009, 7:46:30 PM1/16/09
to clo...@googlegroups.com
That could work, but I think this kind of mechanism should operate at the lowest level "load" bottleneck to ensure it's correctly implemented. Making that low level code aware of a high level concept like *command-line-script* seems to be a less maintainable design than we should strive for.

--Steve

Reply all
Reply to author
Forward
0 new messages