Skip to content
This repository has been archived by the owner on Nov 29, 2021. It is now read-only.

Commit

Permalink
Remove hand-written ScalaSodium boilerplate!
Browse files Browse the repository at this point in the history
Replace with a combo sun.tools + scalameta + scalafmt Frankenstein
monster of a scala script ran from within SBT.

Simply type

    > gensodium

whenever the `sodium.i` file is changed, and `ScalaSodium0.scala`
will be updated as necessary.

Also tweak the setup scripts to work mo' bettah on OS X, for
brainwashed mac users like myself.
  • Loading branch information
hrhino committed Dec 11, 2017
1 parent 2ab5fa4 commit 3a3d3f7
Show file tree
Hide file tree
Showing 10 changed files with 1,934 additions and 1,175 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -5,4 +5,5 @@ t.scala
*.log
tsec-libsodium/jni/*.java
tsec-libsodium/jni/*.c
tsec-libsodium/jni/*.so
tsec-libsodium/jni/*.so
tsec-libsodium/jni/*.dylib
24 changes: 24 additions & 0 deletions project/GenSodiumPlugin.scala
@@ -0,0 +1,24 @@
package tsec.build

object GenSodiumPlugin extends sbt.AutoPlugin {
import sbt._

override def requires = empty
override def trigger = allRequirements

object autoImport {
lazy val gensodium = taskKey[Unit]("Generate ScalaSodium0.scala")
}
import autoImport._

override def buildSettings = Seq(
/* See plugins.sbt for why this is dynamically loaded */
gensodium := {
getClass.getClassLoader
.loadClass("tsec.build.GenSodium")
.getMethod("main", classOf[Array[String]])
.invoke(null, Array.empty[String])
}
)

}
108 changes: 108 additions & 0 deletions project/boiler/gensodium.scala
@@ -0,0 +1,108 @@
package tsec.build

import scala.collection.JavaConverters._

object GenSodium extends App {

val FormatConfig = {
import org.scalafmt.config._
ScalafmtConfig.intellij.copy(
align = Align.none,
newlines = ScalafmtConfig.intellij.newlines.copy(
alwaysBeforeTopLevelStatements = true,
)
)
}

import com.sun.source.{tree => jtree}
import javax.lang.model.`type`.TypeKind
import java.nio.charset._

val jMethods: Seq[jtree.MethodTree] = {
import com.sun.tools.javac._
import tree.{JCTree => tree}
import scala.io._

val source = Source
.fromFile("tsec-libsodium/jni/Sodium.java") // looking here b/c param names not in SodiumJNI
.mkString
val ctx = new util.Context()
new file.JavacFileManager(ctx, true, StandardCharsets.UTF_8)
util.Options.instance(ctx).put(com.sun.tools.javac.main.Option.PARAMETERS, "true")
val parse = parser.ParserFactory
.instance(ctx)
.newParser(source, false, false, false)
val unit = parse.parseCompilationUnit()
val clasz = unit.getTypeDecls.asScala.collectFirst {
case decl: tree.JCClassDecl if decl.name.toString == "Sodium" => decl
}.getOrElse(sys.error("could not find Sodium class"))

clasz.defs.asScala.collect {
case decl: tree.JCMethodDecl => decl
}
}

import scala.meta._

def transType(jt: jtree.Tree): Type = jt match {
case prim: jtree.PrimitiveTypeTree => prim.getPrimitiveTypeKind match {
case TypeKind.BOOLEAN => t"Boolean"
case TypeKind.BYTE => t"Byte"
case TypeKind.SHORT => t"Short"
case TypeKind.INT => t"Int"
case TypeKind.LONG => t"Long"
case TypeKind.CHAR => t"Char"
case TypeKind.FLOAT => t"Float"
case TypeKind.DOUBLE => t"Double"
case TypeKind.VOID => t"Unit"
case other => sys.error(s"Unsupported primitive type kind $other")
}
case arr: jtree.ArrayTypeTree => t"Array[${transType(arr.getType)}]"
}

val sMethods = jMethods.map { jm =>
val params: List[Term.Param] = jm.getParameters.asScala.map { jp =>
val name = Term.Name(jp.getName.toString)
val tpe = transType(jp.getType)
Term.Param(mods = Nil, name = name, decltpe = Some(tpe), default = None)
}.toList
val name = Term.Name(jm.getName.toString)
val tpe = transType(jm.getReturnType)
val args = params.map { p => Term.Name(p.name.value) }
val rhs = q"SodiumJNI.$name(..$args)"
Defn.Def(
mods = Mod.Final() :: Nil,
name = name,
tparams = Nil,
paramss = List(params),
decltpe = Some(tpe),
body = rhs
)
}.sortBy(_.name.value).toList

val noSelfType = Term.Param(Nil, Name.Anonymous(), None, None)

val clsDef = Defn.Class(
mods = Mod.Abstract() :: Mod.Protected(within = Term.Name("tsec")):: Nil,
name = Type.Name("ScalaSodium0"),
tparams = Nil,
ctor = Ctor.Primary(Mod.Private(within = Term.Name("tsec")) :: Nil, name = Ctor.Name("ScalaSodium0"), paramss = Nil),
templ = Template(early = Nil, parents = Nil, self = noSelfType, Some(sMethods))
)

val warning =
s"""/* !!! GENERATED CODE: DO NOT EDIT !!! */
|/* This file is generated by project/boiler/gensodium.scala from tsec-libsodium/jni/sodium.i */
|/* Timestamp: ${java.time.Instant.now.toString} */
|
""".stripMargin

val pkg = Pkg(q"tsec.jni", clsDef :: Nil)

val src = org.scalafmt.Scalafmt.format(warning + pkg.toString, style = FormatConfig).get

import java.io._

new FileOutputStream(new File("tsec-libsodium/src/main/scala/tsec/jni/ScalaSodium0.scala"))
.write(src.getBytes(StandardCharsets.UTF_8))
}
35 changes: 34 additions & 1 deletion project/plugins.sbt
Expand Up @@ -4,4 +4,37 @@ addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1")
addSbtPlugin("com.47deg" % "sbt-microsites" % "0.7.4")
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.5.5")
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.27")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.27")

libraryDependencies ++= List(
//"org.scalameta" %% "scalameta" % "2.1.2",
"com.geirsson" %% "scalafmt-core" % "1.3.0",
"com.geirsson" %% "scalafmt-cli" % "1.3.0",
)

unmanagedJars in Compile ++= {
val log = streams.value.log
sys.env.get("JAVA_HOME").map(jh => new File(s"$jh/lib/tools.jar")) match {
case Some(tools) => Seq(Attributed.blank(tools))
case None =>
log.info(
"""Could not find tools.jar!
| This is surprising, as it is included with the JDK. Maybe look into that.
| If everything else works, comment out gensodium (don't check that in, though!)
""".stripMargin
)
Nil
}
}


sources in Compile ++= {
/* Sad hack for intellij, which doesn't recognize the unmanagedSources key:
* only compile the gensodium script if we're not inside IntelliJ.
* We dynamically load it from the `gensodium` task, so as long as we compile
* it from inside sbt, we'll be fine.
*/
if (!sys.env.getOrElse("XPC_SERVICE_NAME", "").toLowerCase.contains("intellij"))
file("project/boiler/gensodium.scala").getAbsoluteFile :: Nil
else Nil
}
18 changes: 13 additions & 5 deletions sodium_setup.sh
Expand Up @@ -26,17 +26,25 @@ swig -java -package tsec.jni sodium.i
rm -f ../src/main/scala/tsec/jni/SodiumJNI.java
cp SodiumJNI.java ../src/main/scala/tsec/jni

case "$OSTYPE" in
darwin*)
jnilib=libsodiumjni.dylib
destlib=/Library/Java/Extensions
sudo update_dyld_shared_cache
;;
*)
jnilib=libsodiumjni.so
destlib=/usr/lib
sudo ldconfig
;;
esac


jnilib=libsodiumjni.so
destlib=/usr/lib
sudo ldconfig
echo $jnilib
echo $destlib
echo $destlib/$jnilib


gcc -I../usr/local/include/sodium -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux sodium_wrap.c -shared -fPIC -L/usr/local/lib -L/usr/lib -lsodium -o $jnilib
gcc -I../usr/local/include/sodium -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I${JAVA_HOME}/include/darwin sodium_wrap.c -shared -fPIC -L/usr/local/lib -L/usr/lib -lsodium -o $jnilib
sudo rm -f $destlib/$jnilib
sudo cp $jnilib $destlib
rm -f *.java
Expand Down
18 changes: 14 additions & 4 deletions tsec-libsodium/jni/setup.sh 100644 → 100755
Expand Up @@ -23,14 +23,24 @@ export PATH=/usr/local/bin:$PATH
swig -java -package tsec.jni sodium.i


jnilib=libsodiumjni.so
destlib=/usr/lib
sudo ldconfig
case "$OSTYPE" in
darwin*)
jnilib=libsodiumjni.dylib
destlib=/Library/Java/Extensions
sudo update_dyld_shared_cache
;;
*)
jnilib=libsodiumjni.so
destlib=/usr/lib
sudo ldconfig
;;
esac

echo $jnilib
echo $destlib
echo $destlib/$jnilib


gcc -I../usr/local/include/sodium -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux sodium_wrap.c -shared -fPIC -L/usr/local/lib -L/usr/lib -lsodium -o $jnilib
gcc -I../usr/local/include/sodium -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I${JAVA_HOME}/include/darwin sodium_wrap.c -shared -fPIC -L/usr/local/lib -L/usr/lib -lsodium -o $jnilib
sudo rm -f $destlib/$jnilib
sudo cp $jnilib $destlib
4 changes: 4 additions & 0 deletions tsec-libsodium/jni/sodium.i
@@ -1,4 +1,8 @@
/* sodium.i */
/* NOTE WELL that if you change this file, you must update the interface files thusly:
* - run `./setup.sh` to generate the SWIG JNI files
* - run `sbt gensodium` to generate the Scala interface
*/
%module Sodium

%include "typemaps.i"
Expand Down

0 comments on commit 3a3d3f7

Please sign in to comment.