Showing posts with label sbt. Show all posts
Showing posts with label sbt. Show all posts

Sunday, July 04, 2021

gradle to sbt for scala3

Problem and Audience

Scala 3 is an overhaul of the guts of the scala language type system and compiler that was recently released (in mid 2021) for general use. Unfortunately, the scala plugin for gradle does not yet support the new scala-3 build chain, so we ported littleware to scala's sbt build tool.

Porting a gradle build to an sbt build is straight forward. Both gradle and sbt define a graph of tasks for building projects, and they define the task library (types of tasks) via third-party plugins. Both system's define the task instances in a project's build graph with a user-supplied build file written in a DSL that makes API calls against the runtime. For example, the littleware sub-project in both gradle (build.gradle) and sbt (build.sbt) are similar to each other:

sbt:

lazy val littleware = project
  .in(file("littleware"))
  .settings(
    name := "littleware",
    crossPaths := false,
    autoScalaLibrary := false,
    libraryDependencies ++= Seq(
      junit,
      guice,
      guava,
      "javax.mail" % "javax.mail-api" % "1.5.5",
      "javax" % "javaee-web-api" % "8.0",
      "org.apache.derby" % "derby" % "10.15.2.0",
      "org.apache.derby" % "derbyclient" % "10.15.2.0",
      "org.postgresql" % "postgresql" % "42.2.18",
      "mysql" % "mysql-connector-java" % "8.0.23",
      "org.javasimon" % "javasimon-core" % "4.2.0",
      "javax.activation" % "activation" % "1.1.1"
    ) ++ junitRunnerSet ++ log4jJsonSet,
  )

gradle:

project( ':littleware' ) {
    dependencies {
        implementation 'com.google.inject:guice:4.2.3:no_aop@jar'
        implementation 'junit:junit:4.13.2'
        implementation 'com.google.guava:guava:30.1-jre'
        compileOnly 'javax.mail:javax.mail-api:1.5.5'
        implementation 'javax:javaee-web-api:8.0'
        compileOnly 'org.apache.derby:derby:10.15.2.0'
        compileOnly 'org.apache.derby:derbyclient:10.15.2.0'
        compileOnly 'org.postgresql:postgresql:42.2.18'
        compileOnly 'mysql:mysql-connector-java:8.0.23'
        implementation 'org.javasimon:javasimon-core:4.2.0'
        runtimeOnly 'javax.activation:activation:1.1.1'
    }
}

I am glad that the port to SBT was straight forward, but I'm also annoyed that scala has its own build tool, sbt, rather than simply invest in gradle - which is widely used for building java, Android, and kotlin projects. It would be nice if I could just learn gradle, and use it to build scala (version 3) too. The same could be said for golang and dotnet - which also implement their own build tools rather than use gradle, but gradle depends on the jvm, so it makes more sense for languages in that ecosystem. There is a trade-off between building a custom tool chain that is finely tuned for a particular domain, or using a more generic system that has a large community of users. I expect gradle to support scala 3 in a few months anyway, so we will soon have the best of both worlds.

Summary

It was easy to port littleware's gradle build to sbt, since the two systems implement a task-graph design.

Tuesday, July 19, 2016

Gradle with "Service Provider" Pattern

I've started dusting off my old littleware project over the last month or so on a "dev" branch in github. As part of the cleanup I'm moving littleware from its old ANT-IVY build system to Gradle. When I started with ant-ivy it was a great alternative to Maven, and allowed me to extend the build setup put in place by a Netbeans IDE's project with transitive dependencies, jenkins integration, scala support - all that great stuff. The problem is that the ant-ivy setup is idiosyncratic, and Gradle and SBT are well designed, widely used systems. I wound up going with Gradle, because I've read a lot of good things about it, and I want to become more familiar with it, and I had used SBT a little in the past, and found SBT's DSL annoying and its extension mechanisms inscrutable, and of course Google is using Gradle to build Android, ...

Anyway - like I said, I'm dusting off the code, and trying to remember what the hell I was thinking with some of it, but so far the Gradle integration has gone well. One of the issues I'm dealing with is the IVY-centric mechanism littleware had setup to pull in optional run-time dependencies. With Gradle the default behavior of the java plugin is to include all compile-time dependencies at run-time - which is what most people expect. Littleware's ant-ivy setup excluded optional dependencies (like mysql-connector versus postgresql - something like that), and included add-on IVY configurations like "with_mysql", "with_postgres", "with_aws", ... that kind of thing, so a client project would have a dependency specification something like this:

<dependency org="littleware" name="littelware" ... conf="compile->compile;runtime->runtime,with_mysql" />

Of course nobody else works like that, so in the interest of clarity and doing what people expect - I'm going to try to rework things, so that littleware's base jar asset includes service provider interfaces (SPI's), that add-on modules implement, so a client that wants to hook up littleware's ability to wire in a MySQL DataSource to a Guice injector would add a dependency to the 'littleware_mysql_module' to their build - something like that. The Jackson JSON parser implements a module system like this, and the SPI pattern is all over java-EE (JDBC, Servlets, whatever); it's a good and widely understood pattern. We'll see how it goes.