├── .gitignore
├── LICENSE
├── README.md
├── build.sbt
├── project
├── build.properties
└── plugins.sbt
└── src
└── main
└── scala
├── ClojureC.scala
├── ClojurePlugin.scala
└── Keys.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 |
4 | # sbt specific
5 | dist/*
6 | target/
7 | lib_managed/
8 | src_managed/
9 | project/boot/
10 | project/plugins/project/
11 |
12 | # Scala-IDE specific
13 | .scala_dependencies
14 |
15 | .cache
16 |
17 | .classpath
18 |
19 | .project
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Geoffroy Couprie
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | sbt-clojure
2 | ===========
3 |
4 | an sbt plugin for clojure, inspired from [sbt-groovy](https://github.com/fupelaqu/sbt-groovy).
5 |
6 | To use Clojure code in your Scala project, follow these few steps:
7 |
8 | 1. add this to build.sbt
9 |
10 | ```
11 | seq(clojure.settings :_*)
12 |
13 | libraryDependencies += "org.clojure" % "clojure" % "1.5.1"
14 | ```
15 |
16 | 2. add this to projectt/plugins.sbt
17 |
18 | ```
19 | addSbtPlugin("com.unhandledexpression" % "sbt-clojure" % "0.1")
20 | ```
21 |
22 | 3. Create a clojure directory to hold the sources
23 |
24 | Example:
25 |
26 | ```
27 | src/
28 | └── main
29 | ├── clojure
30 | │ ├── hello.clj
31 | │ └── sub
32 | │ └── num.clj
33 | └── scala
34 | └── main.scala
35 | ```
36 |
37 | An [example project](https://github.com/Geal/sbt-clojure-example) is available for reference.
38 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | sbtPlugin := true
2 |
3 | name := "sbt-clojure"
4 |
5 | organization := "com.unhandledexpression"
6 |
7 | version := "0.1"
8 |
9 | scalaVersion := "2.10.2"
10 |
11 | publishMavenStyle := true
12 |
13 | publishTo := {
14 | val nexus = "https://oss.sonatype.org/"
15 | if (version.value.trim.endsWith("SNAPSHOT"))
16 | Some("snapshots" at nexus + "content/repositories/snapshots")
17 | else
18 | Some("releases" at nexus + "service/local/staging/deploy/maven2")
19 | }
20 |
21 | libraryDependencies += "org.clojure" % "clojure" % "1.5.1"
22 |
23 | publishArtifact in Test := false
24 |
25 | pomIncludeRepository := { _ => false }
26 |
27 | pomExtra := (
28 | https://github.com/Geal/sbt-clojure
29 |
30 |
31 | MIT
32 | http://opensource.org/licenses/mit-license.php
33 | repo
34 |
35 |
36 |
37 | git@github.com:Geal/sbt-clojure.git
38 | scm:git:git@github.com:Geal/sbt-clojure.git
39 |
40 |
41 |
42 | gcouprie
43 | Geoffroy Couprie
44 | http://geoffroycouprie.com
45 |
46 | )
47 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbtVersion=0.13.0
2 |
3 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.3.0")
2 |
3 |
--------------------------------------------------------------------------------
/src/main/scala/ClojureC.scala:
--------------------------------------------------------------------------------
1 | package com.unhandledexpression.sbtclojure
2 |
3 | import sbt._
4 | import sbt.Keys._
5 | import java.io.File
6 |
7 | import sbt.classpath.ClasspathUtilities
8 |
9 | class ClojureC(val classpath : Seq[File], val sourceDirectory : File, val stubDirectory : File, val destinationDirectory : File) {
10 |
11 | lazy val oldContextClassLoader = Thread.currentThread.getContextClassLoader
12 |
13 | lazy val classLoader = ClasspathUtilities.toLoader(classpath)
14 | lazy val clojureClass = classLoader.loadClass("org.clojure.core")
15 | lazy val rt = classLoader.loadClass("clojure.lang.RT")
16 | lazy val varClass = classLoader.loadClass("clojure.lang.Var")
17 | lazy val varFunction = rt.getDeclaredMethod("var", classOf[java.lang.String], classOf[java.lang.String])
18 | lazy val loadResourceFunction = rt.getDeclaredMethod("loadResourceScript", classOf[java.lang.String])
19 | lazy val rtLoadFunction = rt.getDeclaredMethod("load", classOf[java.lang.String])
20 | lazy val rtInitFunction = rt.getDeclaredMethod("init")
21 | lazy val rtMap = rt.getDeclaredMethod("map", classOf[Array[Object]])
22 |
23 | def getRecursiveListOfFiles(dir: File): Array[File] = {
24 | val files = dir.listFiles
25 | files ++ files.filter(_.isDirectory).flatMap(getRecursiveListOfFiles)
26 | }
27 |
28 | def compile() : Unit = {
29 | val files = getRecursiveListOfFiles(sourceDirectory)
30 |
31 | IO.createDirectory(sourceDirectory)
32 | IO.createDirectory(destinationDirectory)
33 | try{
34 | Thread.currentThread().setContextClassLoader(classLoader)
35 | rtInitFunction.invoke(null)
36 | val compilerClass = classLoader.loadClass("clojure.lang.Compiler")
37 | val loadFunction = compilerClass.getDeclaredMethod("load", classOf[java.io.Reader])
38 | val compileFunction = compilerClass.getDeclaredMethod("compile", classOf[java.io.Reader], classOf[java.lang.String], classOf[java.lang.String])
39 |
40 | val associativeClass = classLoader.loadClass("clojure.lang.Associative")
41 | val pushTBFunction = varClass.getDeclaredMethod("pushThreadBindings", associativeClass)
42 | val popTBFunction = varClass.getDeclaredMethod("popThreadBindings")
43 |
44 | val compilePath = varFunction.invoke(null, "clojure.core", "*compile-path*")
45 | val compileFiles = varFunction.invoke(null, "clojure.core", "*compile-files*")
46 |
47 | val newMap = rtMap.invoke(null, Array(compilePath, destinationDirectory.getAbsolutePath(), compileFiles, true:java.lang.Boolean))
48 | pushTBFunction.invoke(null, newMap)
49 |
50 | files.filter(!_.isDirectory).map { f =>
51 | //val suffix = f.toString.stripPrefix(sourceDirectory.toString)
52 | compileFunction.invoke(null, new java.io.FileReader(f.getAbsolutePath()), f.getName(), f.getName())
53 | }
54 |
55 | popTBFunction.invoke(null)
56 | }
57 | finally{
58 | Thread.currentThread.setContextClassLoader(oldContextClassLoader)
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/scala/ClojurePlugin.scala:
--------------------------------------------------------------------------------
1 | package com.unhandledexpression.sbtclojure
2 |
3 | import sbt._
4 | import Keys._
5 | import java.io.File
6 |
7 | object ClojurePlugin extends Plugin {
8 |
9 | private object ClojureDefaults extends Keys {
10 | val settings = Seq(
11 | clojureVersion := "1.5.1",
12 | libraryDependencies ++= Seq[ModuleID](
13 | "org.clojure" % "clojure" % clojureVersion.value % Config.name
14 | )
15 | )
16 | }
17 |
18 | object clojure extends Keys {
19 | val settings = Seq(ivyConfigurations += Config) ++ ClojureDefaults.settings ++ Seq(
20 | clojureSource in Compile := (sourceDirectory in Compile).value / "clojure",
21 | unmanagedResourceDirectories in Compile += {(clojureSource in Compile).value},
22 | clojurec in Compile := {
23 | val s: TaskStreams = streams.value
24 | val sourceDirectory : File = (clojureSource in Compile).value
25 | val nb = (sourceDirectory ** "*.clj").get.size
26 | if(nb > 0){
27 | val s: TaskStreams = streams.value
28 | s.log.info("Start Compiling Clojure sources")
29 | val classpath : Seq[File] = update.value.select( configurationFilter(name = "*") ) ++ Seq((classDirectory in Compile).value)
30 | val stubDirectory : File = (sourceManaged in Compile).value
31 | val destinationDirectory : File = (classDirectory in Compile).value
32 |
33 | def clojureClazz(file : File) : File = {
34 | val p = file.getAbsolutePath()
35 | new File(destinationDirectory.getAbsolutePath() + p.substring(sourceDirectory.getAbsolutePath().length(), p.length() - ".clj".length()) + ".class")
36 | }
37 |
38 | (sourceDirectory ** "*.clj").get map (clojureClazz) foreach {f => if(f.exists()){IO.delete(f)}}
39 |
40 | new ClojureC(classpath, sourceDirectory, stubDirectory, destinationDirectory).compile
41 | }
42 | },
43 | compile in Compile <<= (compile in Compile) dependsOn (clojurec in Compile)
44 | )
45 | }
46 |
47 | object testClojure extends TestKeys {
48 | val settings = Seq(ivyConfigurations += Config) ++ inConfig(Config)(Defaults.testTasks ++ ClojureDefaults.settings ++ Seq(
49 | definedTests <<= definedTests in Test,
50 | definedTestNames <<= definedTestNames in Test,
51 | fullClasspath <<= fullClasspath in Test,
52 |
53 | clojureSource in Test := (sourceDirectory in Test).value / "clojure",
54 | unmanagedResourceDirectories in Test += {(clojureSource in Test).value},
55 | clojurec in Test := {
56 | val sourceDirectory : File = (clojureSource in Test).value
57 | val nb = (sourceDirectory ** "*.clj").get.size
58 | if(nb > 0){
59 | val s: TaskStreams = streams.value
60 | s.log.info("Start Compiling Test Clojure sources")
61 | val classpath : Seq[File] = update.value.select( configurationFilter(name = "*") ) ++ Seq((classDirectory in Test).value) ++ Seq((classDirectory in Compile).value)
62 | val stubDirectory : File = (sourceManaged in Test).value
63 | val destinationDirectory : File = (classDirectory in Test).value
64 |
65 | def clojureClazz(file : File) : File = {
66 | val p = file.getAbsolutePath()
67 | new File(destinationDirectory.getAbsolutePath() + p.substring(sourceDirectory.getAbsolutePath().length(), p.length() - ".clj".length()) + ".class")
68 | }
69 |
70 | (sourceDirectory ** "*.clj").get map (clojureClazz) foreach {f => if(f.exists()){IO.delete(f)}}
71 |
72 | new ClojureC(classpath, sourceDirectory, stubDirectory, destinationDirectory).compile
73 | }
74 | },
75 | clojurec in Test <<= (clojurec in Test) dependsOn (compile in Test),
76 | test in Test <<= (test in Test) dependsOn (clojurec in Test)
77 | ))
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/scala/Keys.scala:
--------------------------------------------------------------------------------
1 | package com.unhandledexpression.sbtclojure
2 |
3 | import sbt._
4 | import sbt.Keys._
5 | import java.io.File
6 |
7 | trait Keys {
8 |
9 | lazy val Config = config("clojure") extend(Compile) hide
10 | lazy val clojureVersion = settingKey[String]("Clojure version")
11 | lazy val clojureSource = settingKey[File]("Default Clojure source directory")
12 | lazy val clojurec = taskKey[Unit]("Compile Clojure sources")
13 |
14 | }
15 |
16 | trait TestKeys extends Keys {
17 | override lazy val Config = config("test-clojure") extend(Test) hide
18 | }
19 |
20 | trait IntegrationTestKeys extends TestKeys {
21 | override lazy val Config = config("it-clojure") extend(IntegrationTest) hide
22 | }
23 |
--------------------------------------------------------------------------------