New clojure support in Polyglot Maven

67 views
Skip to first unread message

Antony Blakey

unread,
Apr 7, 2010, 8:07:23 PM4/7/10
to poly...@maven.org, clo...@googlegroups.com
I've just pushed a major update to the Clojure support in pmaven to http://github.com/sonatype/polyglot-maven. It now covers 100% of maven by reflecting over the maven object model. Examples are in the tests and in the reader.clj source file. It includes leiningen support e.g. mvn -f project.clj install should work.

Antony Blakey
-------------
CTO, Linkuistics Pty Ltd
Ph: 0438 840 787

The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.
-- Martin Luther King


Jarkko Oranen

unread,
Apr 8, 2010, 4:15:41 AM4/8/10
to Clojure
Looks cool. This should help the XML-allergic :)

Though, is there a reason why all symbol arguments to defmodel have to
be quoted? It looks rather unpleasant. Seems like you should be able
to fix that by changing the body of defmaven to
`(reset! *MODEL* (Model ~@(for [a args] `(quote ~a))))

Another possible approach is to wrap the free key-value pairs in a map
or a vector so that it's easy to quote the whole form.

Antony Blakey

unread,
Apr 8, 2010, 6:04:42 AM4/8/10
to clo...@googlegroups.com

The point is that the unlike defproject, the body is assumed to be executable content, rather than just data. And you can do

:dependencies '[[org.clojure/clojure-contrib "1.2.0-SNAPSHOT"]
...
[hiccup "0.2.3"]]

which reduces the amount of quoting.

In fact, this defmaven form:

(defmaven 'org.clojure/clojure "1.2.0-master-SNAPSHOT"
:model-version "4.0.0"
:name "Lamdras Website"
:description "Acumen / LRMDS Integration"
:properties { :project.build.sourceEncoding "UTF-8" }
:packaging "war"
:dependencies [['ring/ring-servlet "0.2.0-RC2"]
['ring/ring-devel "0.2.0-RC2"]
['clj-routing/ "0.1.0-SNAPSHOT"]
['clout "0.2.0-SNAPSHOT"]
['compojure "0.4.0-SNAPSHOT"]
['hiccup "0.1.0-SNAPSHOT"]
'org.clojure/clojure
'org.clojure/clojure-contrib
'congomongo/congomongo]
:provided-dependencies [['org.mortbay.jetty/servlet-api-2.5 "6.1.14"]]
:build [:final-name "website"
:plugins [['org.apache.maven.plugins/maven-compiler-plugin "2.1" :source "1.6" :target "1.6"]
['com.theoryinpractise/clojure-maven-plugin "1.3.1"
:sourceDirectories ["src/main/java"]
:executions [[:id "compile-clojure" :phase "compile" :goals ["compile"]]]]
['org.mortbay.jetty/maven-jetty-plugin "6.1.10"
:scanIntervalSeconds 10 :stopKey "foo" :stopPort 9999]]])

has an equivalent, canonical representation something like this:

(Model
:group-id "org.clojure" :artifact-id "clojure" :version "1.2.0-master-SNAPSHOT"
:model-version "4.0.0"
:name "Lamdras Website"
:description "Acumen / LRMDS Integration"
:properties { :project.build.sourceEncoding "UTF-8" }
:packaging "war"
:dependencies [(Dependency :group-id "ring" :artifact-id "ring-servlet" :version "0.2.0-RC2")
(Dependency :group-id "ring" :artifact-id "ring-devel" :version "0.2.0-RC2")
(Dependency :group-id "clj-routing" :artifact-id "clj-routing" :version "0.1.0-SNAPSHOT")
(Dependency :group-id "clout" :artifact-id "clout" :version "0.2.0-SNAPSHOT")
(Dependency :group-id "compojure" :artifact-id "compojure" :version "0.4.0-SNAPSHOT")
(Dependency :group-id "hiccup" :artifact-id "hiccup" :version "0.1.0-SNAPSHOT")
(Dependency :group-id "org.clojure" :artifact-id "clojure")
(Dependency :group-id "org.clojure" :artifact-id "clojure-contrib")
(Dependency :group-id "congomongo" :artifact-id "congomongo")
(Dependency :group-id "org.mortbay.jetty" :artifact-id "servlet-api-2.5" :version "6.1.14" scope: "provided")]
:build (Build
:final-name "website"
:plugins [(Plugin :group-id "org.apache.maven.plugins" :artifact-id "maven-compiler-plugin" :version "2.1"
:configuration: [[:source "1.6"] [:target "1.6"]])
(Plugin :group-id "com.theoryinpractise" :artifact-id "clojure-maven-plugin" :version "1.3.1"
:configuration [[:sourceDirectories [[:sourceDirectory "src/main/java"]]]]
:executions [(Execution :id "compile-clojure" :phase "compile" :goals ["compile"])])
(Plugin :group-id "org.mortbay.jetty" :artifact-id "maven-jetty-plugin" :version "6.1.10"
:configuration [[:scanIntervalSeconds 10] [:stopKey "foo"] [:stopPort 9999]])]))

where Model, Dependency, Build,. Plugin, Execution (and many more) are functions that create e.g. org.apache.maven.model.Model and set the properties from key/value arguments. There are a variety of shortcuts built into the functions e.g. knowing the type of each field - which for the maven model is even possible for List<> generics due to the presence of parallel addXXX(YYY) methods for every setXXX(List) which gives you the erased parameter of the list generic - allows you to elide the type constructor. The :configuration elements are more complicated than I've shown because they need to generate nearly fill XML with attributes and namespaces (but not mixed content).

Hopefully you can see that this syntax falls out of the direct construction of maven Model object, unmediated by intermediate syntax or data structuring. So there's a good reason for the way it looks.

Antony Blakey
-------------
CTO, Linkuistics Pty Ltd
Ph: 0438 840 787

A reasonable man adapts himself to suit his environment. An unreasonable man persists in attempting to adapt his environment to suit himself. Therefore, all progress depends on the unreasonable man.
-- George Bernard Shaw


Jarkko Oranen

unread,
Apr 8, 2010, 7:39:59 AM4/8/10
to Clojure

> Hopefully you can see that this syntax falls out of the direct construction of maven Model object, unmediated by intermediate syntax or data structuring. So there's a good reason for the way it looks.
>

Right. Thanks for the thorough explanation. It's not so bad if you
quote the vectors instead of the individual symbols. However, it seems
to me that defmaven could very well be a plain function in that case
(just (reset! *MODEL* (apply Model args))). :)

Antony Blakey

unread,
Apr 8, 2010, 8:15:50 AM4/8/10
to clo...@googlegroups.com

Yes, you're right. I'll make that change.

Antony Blakey
-------------
CTO, Linkuistics Pty Ltd
Ph: 0438 840 787

The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.
-- Martin Luther King


Armando Blancas

unread,
Apr 8, 2010, 4:57:40 PM4/8/10
to Clojure
> Looks cool. This should help the XML-allergic :)

Though I don't like it, the XML is the least of my problems. Don't
know what to do or even where to start. I want to do the following in
maven or pmaven, but anything beyond their Hello World example has
been a real struggle :-( Any pointers?

-- Generate Java code from an ANTLR lexer:
java -cp ... org.antlr.Tool Scanner.g

-- Compile the scanner and an exception class:
javac -cp ... Scanner.java ExitException.java

-- Compile the clojure program using the above classes
java -Dclojure.compile.path=... -cp ... clojure.lang.Compile prog.main

-- Package incl. the antlr runtime inside the jar
jar -x ...
jar cMf prog.jar ... *.class

- clean up: delete the generated Java classes, the .tokens file, the
expanded antl runtime, all .class files

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Aaron Cohen

unread,
Apr 8, 2010, 5:59:24 PM4/8/10
to clo...@googlegroups.com
On Thu, Apr 8, 2010 at 4:57 PM, Armando Blancas
<armando...@yahoo.com> wrote:
>> Looks cool. This should help the XML-allergic :)
>
> Though I don't like it, the XML is the least of my problems. Don't
> know what to do or even where to start. I want to do the following in
> maven or pmaven, but anything beyond their Hello World example has
> been a real struggle :-(    Any pointers?
>

The steps are:
Add antlr3-maven-plugin to your Project Object Model (pom).

In maven xml it looks like:

<project>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

I'm guessing that in the new polyglot world it would look something like:

(defmaven
; ... other stuff that defines your project name, version, etc
:build [:plugins [['org.codehaus.mojo/antlr3-maven-plugin "1.0"
:executions [[:goals ["antlr"]]]]]]]) ; Who
knows if I got the braces correct


Which already seems like a pretty big improvement to me.

As a result you would be able to:

(The following all assumes that your *.g files are in src/main/antlr,
where maven looks by default, more details at:
http://mojo.codehaus.org/antlr3-maven-plugin/)
> -- Generate Java code from an ANTLR lexer:
> java -cp ...  org.antlr.Tool Scanner.g

mvn -f project.clj generate-sources

(the resulting java files end up in the
"target/generated-sources/antlr" directory by default)

> -- Compile the scanner and an exception class:
> javac -cp ... Scanner.java ExitException.java

I'm not sure how you'd do this in a maven-only world. I guess you'd
have to do "java -cp target/generated-sources/antlr/*.java", not sure
why you'd want to though.

I guess one way would be to put your antlr stuff in its own
sub-project and then "mvn -f project.clj compile".

> -- Compile the clojure program using the above classes
> java -Dclojure.compile.path=... -cp ... clojure.lang.Compile prog.main

mvn -f project.clj compile

(You can also skip straight to this if you want, the antlr files
should be processed automatically into java for you, you'd also need
to add the maven-clojure-plugin to your plugins to be able to do
compile clojure code and do something like "mvn repl")

> -- Package incl. the antlr runtime inside the jar
> jar -x ...
> jar cMf prog.jar ... *.class

mvn -f project.clj package

(The resulting jar file ends up in
"target\project_name-module_name-<version>.jar by default. A file
called "target\project_name-module-name-<version>-sources.jar" is also
automatically created containing all your source code. I don't think,
however, that it adds the antlr runtime to your jar)

For more complicated packaging (such as adding the antlr runtime),
you'd use the maven-assembly-plugin to add arbitrary stuff to your
resulting jar. It's a little more complicated, and requires its own
xml.
http://maven.apache.org/plugins/maven-assembly-plugin/

> - clean up: delete the generated Java classes, the .tokens file, the
> expanded antl runtime, all .class files

mvn -f project.clj clean

Armando Blancas

unread,
Apr 8, 2010, 7:48:43 PM4/8/10
to Clojure
Thanks for your response. Since that's already more than I can chew
I'll stick to regular pom files as I try to follow your directions and
look through docs and samples.

Graham Fawcett

unread,
Apr 9, 2010, 10:54:43 AM4/9/10
to clo...@googlegroups.com
Hi Antony,

On Wed, Apr 7, 2010 at 8:07 PM, Antony Blakey <antony...@gmail.com> wrote:
>
> I've just pushed a major update to the Clojure support in pmaven to
> http://github.com/sonatype/polyglot-maven. It now covers 100% of
> maven by reflecting over the maven object model. Examples are in the
> tests and in the reader.clj source file. It includes leiningen
> support e.g. mvn -f project.clj install should work.

Thank you, this is a very interesting piece of work.

Your building/installation instructions are a bit on the terse side,
and left this Maven newbie in trial-and-error-land. :) Particularly
this statement confused me, because I don't know what an "assembly
play" is, or why I'd want one:

After this completes, you can unzip the assembly play with polyglot
maven:

unzip pmaven-cli/target/pmaven-*-bin.zip
./pmaven-cli*/bin/mvn

Or did you mean "unzip the assembly, and play with polyglot maven"?

After trial-and-erroring, I gathered that after unzipping I ended up
with a 'polyglot maven' directory whose 'bin/mvn' could be used to run
tasks like 'mvn -f project.clj'. I assume that the Maven 2 instance I
used to build polyglot-maven is not modified in any way (e.g. it
wasn't retooled to be able to run 'mvn -f project.clj'). Is that
correct?

To use this on an ongoing basis, should I should unzip polyglot-maven
somewhere stable, and then just add the new 'polylot-maven/bin' to my
PATH? (That may seem a silly question, but maybe there's a more
Mavenish way to do these things, and I just don't know about it.)

One last point -- I tried 'mvn -f project.clj' on one of my existing
Leiningen projects, and it failed because the project.clj contained
some clauses that Polyglot Maven didn't expect (:main, :compile-path).
These may not be relevant in a Maven build, but it would be nice if
they could exist in the project.clj file without causing an error when
using your tool. You've gone so far with Leiningen support already,
this would be a valuable next step.

Regards,
Graham


>
> Antony Blakey
> -------------
> CTO, Linkuistics Pty Ltd
> Ph: 0438 840 787
>
> The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.
>  -- Martin Luther King
>
>

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>

> To unsubscribe, reply using "remove me" as the subject.
>

Antony Blakey

unread,
Apr 9, 2010, 11:10:06 AM4/9/10
to clo...@googlegroups.com

On 10/04/2010, at 12:24 AM, Graham Fawcett wrote:

> Your building/installation instructions are a bit on the terse side,
> and left this Maven newbie in trial-and-error-land. :)

I haven't written any instructions, but yes, the instructions on the project aren't extensive. And the documentation that is on the sonatype site for clojure support is out of date because I don't yet have write access to the wiki yet. Sorry, I know software without good documentation is only half done.

> Particularly
> this statement confused me, because I don't know what an "assembly
> play" is, or why I'd want one:
>
> After this completes, you can unzip the assembly play with polyglot
> maven:
>
> unzip pmaven-cli/target/pmaven-*-bin.zip
> ./pmaven-cli*/bin/mvn
>
> Or did you mean "unzip the assembly, and play with polyglot maven"?

Yes. Actually, this is a bad use of the term because 'assembly' means something specific in maven land i.e. the assembly plugin that does Leiningen uberjar kind of things (on steroids, of course).

> After trial-and-erroring, I gathered that after unzipping I ended up
> with a 'polyglot maven' directory whose 'bin/mvn' could be used to run
> tasks like 'mvn -f project.clj'. I assume that the Maven 2 instance I
> used to build polyglot-maven is not modified in any way (e.g. it
> wasn't retooled to be able to run 'mvn -f project.clj'). Is that
> correct?

No it doesn't touch existing maven installations, polyglot maven currently includes maven 3.0 alpha 7.

> To use this on an ongoing basis, should I should unzip polyglot-maven
> somewhere stable, and then just add the new 'polylot-maven/bin' to my
> PATH? (That may seem a silly question, but maybe there's a more
> Mavenish way to do these things, and I just don't know about it.)

No, that's right. Although be aware that there are a few things that Maven 3.0 isn't backward compatible with yet. In particular I can't use the Atlassian plugin.

> One last point -- I tried 'mvn -f project.clj' on one of my existing
> Leiningen projects, and it failed because the project.clj contained
> some clauses that Polyglot Maven didn't expect (:main, :compile-path).
> These may not be relevant in a Maven build, but it would be nice if
> they could exist in the project.clj file without causing an error when
> using your tool. You've gone so far with Leiningen support already,
> this would be a valuable next step.

Yes, the extensive and complete Leiningen documentation indicates that there are a number of things not covered in my defproject macro :) I waver between thinking I should ignore properties I don't understand and wanting to deal with everything I possibly can, by treating unknowns as errors because you never know when that unknown property really is important. OTOH, I don't deal with Leiningen plugins and have no plans to, so maybe in this case "ignorance is bliss" is the best strategy.

Antony Blakey
--------------------------


CTO, Linkuistics Pty Ltd
Ph: 0438 840 787

Lack of will power has caused more failure than lack of intelligence or ability.
-- Flower A. Newhouse

Graham Fawcett

unread,
Apr 9, 2010, 11:31:27 AM4/9/10
to clo...@googlegroups.com
Hi Antony,

On Fri, Apr 9, 2010 at 11:10 AM, Antony Blakey <antony...@gmail.com> wrote:
>
> On 10/04/2010, at 12:24 AM, Graham Fawcett wrote:
>
>> Your building/installation instructions are a bit on the terse side,
>> and left this Maven newbie in trial-and-error-land. :)
>
> I haven't written any instructions, but yes, the instructions on the
> project aren't extensive. And the documentation that is on the
> sonatype site for clojure support is out of date because I don't yet
> have write access to the wiki yet. Sorry, I know software without
> good documentation is only half done.

No worries! It's an early release, and I'm happy that you didn't wait
to announce the software for lack of documentation. Hopefuly the
feedback you get from the list might help you to (eventually) write
some documentation for the Clojure-oriented (and Maven-disoriented!)
audience.

>> To use this on an ongoing basis, should I should unzip polyglot-maven
>> somewhere stable, and then just add the new 'polylot-maven/bin' to my
>> PATH? (That may seem a silly question, but maybe there's a more
>> Mavenish way to do these things, and I just don't know about it.)
>
> No, that's right. Although be aware that there are a few things that
> Maven 3.0 isn't backward compatible with yet. In particular I can't
> use the Atlassian plugin.

OK, thanks for clarifying this.

>> One last point -- I tried 'mvn -f project.clj' on one of my existing
>> Leiningen projects, and it failed because the project.clj contained
>> some clauses that Polyglot Maven didn't expect (:main, :compile-path).
>> These may not be relevant in a Maven build, but it would be nice if
>> they could exist in the project.clj file without causing an error when
>> using your tool. You've gone so far with Leiningen support already,
>> this would be a valuable next step.
>
> Yes, the extensive and complete Leiningen documentation indicates
> that there are a number of things not covered in my defproject macro
> :) I waver between thinking I should ignore properties I don't
> understand and wanting to deal with everything I possibly can, by
> treating unknowns as errors because you never know when that unknown
> property really is important. OTOH, I don't deal with Leiningen
> plugins and have no plans to, so maybe in this case "ignorance is
> bliss" is the best strategy.

That's a good point. I've only been a casual Leiningen user, so I
can't comment much on the tradeoffs, but I respect that you don't want
to get too deep into Leiningen-compatibility waters. In the short
term, consider documenting that some project.clj clauses may cause
build errors.

Best,
Graham

>
> Antony Blakey
> --------------------------
> CTO, Linkuistics Pty Ltd
> Ph: 0438 840 787
>
> Lack of will power has caused more failure than lack of intelligence or ability.
>  -- Flower A. Newhouse
>

Reply all
Reply to author
Forward
0 new messages