├── project
├── build.properties
├── plugins.sbt
├── Dependencies.scala
└── Settings.scala
├── modules
└── sbt-swagger-2
│ └── src
│ ├── sbt-test
│ └── sbt-swagger-2
│ │ └── simple
│ │ ├── test
│ │ ├── project
│ │ ├── build.properties
│ │ └── plugins.sbt
│ │ ├── build.sbt
│ │ └── src
│ │ └── main
│ │ └── scala
│ │ └── Main.scala
│ └── main
│ └── scala
│ └── io
│ └── scalaland
│ └── sbtswagger2
│ ├── SwaggerOutput.scala
│ └── SbtSwagger2Plugin.scala
├── version.sbt
├── .gitignore
├── .editorconfig
├── .travis.yml
├── .jvmopts
├── .scalafmt.conf
├── sbt-swagger-2.sbt
├── README.md
├── LICENSE
└── sbt
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.1.6
2 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/sbt-test/sbt-swagger-2/simple/test:
--------------------------------------------------------------------------------
1 | > run
2 | > swaggerGenerate
3 |
--------------------------------------------------------------------------------
/version.sbt:
--------------------------------------------------------------------------------
1 | import com.typesafe.sbt.SbtGit.git.baseVersion
2 |
3 | baseVersion := "0.1"
4 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/sbt-test/sbt-swagger-2/simple/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.1.6
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | data/
3 | target/
4 | project/target
5 | *.jar
6 | *.log
7 | *.out
8 | *.txt
9 | *~
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 |
11 | max_line_length = 120
12 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/sbt-test/sbt-swagger-2/simple/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | {
2 | val pluginVersion = System.getProperty("plugin.version")
3 | if(pluginVersion == null)
4 | throw new RuntimeException("""|The system property 'plugin.version' is not defined.
5 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
6 | else addSbtPlugin("io.scalaland" % """sbt-swagger-2""" % pluginVersion)
7 | }
8 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
2 |
3 | addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15")
4 |
5 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.9.3")
6 |
7 | addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.2.1")
8 |
9 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
10 |
11 | libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.7.25"
12 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/sbt-test/sbt-swagger-2/simple/build.sbt:
--------------------------------------------------------------------------------
1 | version := "0.1"
2 | scalaVersion := "2.12.6"
3 |
4 | mainClass := Some("simple.Main")
5 |
6 | swaggerOutputs += Swagger.Output(
7 | output = resourceManaged.value / "swagger.json",
8 | host = "http://localhost",
9 | schemes = List(Swagger.Scheme.HTTP, Swagger.Scheme.HTTPS),
10 | info = Swagger.Info(
11 | title = "Simple API",
12 | version = version.value,
13 | description = "Simple description"
14 | )
15 | )
16 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/sbt-test/sbt-swagger-2/simple/src/main/scala/Main.scala:
--------------------------------------------------------------------------------
1 | package simple
2 |
3 | import io.swagger.annotations._
4 |
5 | /**
6 | * A simple class and objects to write tests against.
7 | */
8 | @Api("Test API")
9 | class Main {
10 | val default = "the function returned"
11 | def method = default + " " + Main.function
12 | }
13 |
14 | object Main {
15 |
16 | val constant = 1
17 | def function = 2*constant
18 |
19 | def main(args: Array[String]): Unit = {
20 | println(new Main().default)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | scala:
3 | - 2.12.6
4 | jdk:
5 | - openjdk8
6 | - oraclejdk8
7 | dist: trusty
8 | cache:
9 | directories:
10 | - $HOME/.ivy2/cache
11 | - $HOME/.sbt/boot/
12 | script: ./sbt scripted
13 | after_script:
14 | - find $HOME/.sbt -name "*.lock" | xargs rm
15 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm
16 | notifications:
17 | email:
18 | on_success: never
19 | on_failure: change
20 |
--------------------------------------------------------------------------------
/.jvmopts:
--------------------------------------------------------------------------------
1 | # Mostly borrowed from Cats - if it's good enough for them, it's good enough for us
2 |
3 | # see https://weblogs.java.net/blog/kcpeppe/archive/2013/12/11/case-study-jvm-hotspot-flags
4 | -Dfile.encoding=UTF8
5 | -Xms1G
6 | # limited by Shippable's RAM size (3.75G total)
7 | -Xmx2G
8 | # without that compiler throws stack overflow on implicit resolution
9 | -Xss8m
10 | -XX:ReservedCodeCacheSize=250M
11 | -XX:+TieredCompilation
12 | -XX:-UseGCOverheadLimit
13 | # effectively adds GC to Perm space
14 | -XX:+CMSClassUnloadingEnabled
15 | # must be enabled for CMSClassUnloadingEnabled to work
16 | #-XX:+UseConcMarkSweepGC
17 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | maxColumn = 120
2 | style = IntelliJ
3 | align = most
4 | align.openParenCallSite = true
5 | align.tokens.add = ["=",":"]
6 | assumeStandardLibraryStripMargin = true
7 | newlines.sometimesBeforeColonInMethodReturnType = false
8 | optIn.breakChainOnFirstMethodDot = false
9 | optIn.configStyleArguments = true
10 | runner.optimizer.forceConfigStyleOnOffset = 120
11 | runner.optimizer.forceConfigStyleMinArgCount = 1
12 | spaces.inImportCurlyBraces = true
13 |
14 | rewrite.rules = [
15 | PreferCurlyFors,
16 | RedundantBraces,
17 | RedundantParens,
18 | SortImports
19 | ]
20 |
21 | lineEndings = unix
22 |
23 | project.git = true
24 | project.includeFilters = [
25 | "modules/.+\\.sbt",
26 | "modules/.+\\.scala",
27 | "project/.+\\.sbt",
28 | "project/.+\\.scala",
29 | ".+\\.sbt"
30 | ]
31 |
--------------------------------------------------------------------------------
/sbt-swagger-2.sbt:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Settings._
3 |
4 | lazy val build = project.root
5 | .setName("sbt plugin")
6 | .setDescription("Build of an sbt Swagger 2 plugin")
7 | .configureRoot
8 | .settings(noPublishSettings: _*)
9 | .aggregate(sbtSwagger2)
10 |
11 | lazy val sbtSwagger2 = project
12 | .module("sbt-swagger-2")
13 | .setName("sbt-swagger-2")
14 | .setDescription("sbt plugin for generating Swagger JSON schemas during build")
15 | .setInitialCommand("_")
16 | .configureModule
17 | .settings(publishSettings: _*)
18 | .settings(
19 | sbtPlugin := true,
20 | scriptedLaunchOpts ++= Seq("-Xmx1024M", "-Dplugin.version=" + version.value),
21 | scriptedBufferLog := false
22 | )
23 |
24 | lazy val publishSettings = Seq(
25 | organization := "io.scalaland",
26 | homepage := Some(url("https://scalaland.io")),
27 | licenses := Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
28 | scmInfo := Some(
29 | ScmInfo(url("https://github.com/scalalandio/sbt-swagger-2"), "scm:git:git@github.com:scalalandio/sbt-swagger-2.git")
30 | ),
31 | publishTo := {
32 | val nexus = "https://oss.sonatype.org/"
33 | if (isSnapshot.value)
34 | Some("snapshots" at nexus + "content/repositories/snapshots")
35 | else
36 | Some("releases" at nexus + "service/local/staging/deploy/maven2")
37 | },
38 | publishMavenStyle := true,
39 | publishArtifact in Test := false,
40 | pomIncludeRepository := { _ =>
41 | false
42 | },
43 | pomExtra := (
44 |
45 |
46 | krzemin
47 | Piotr Krzemiński
48 | http://github.com/krzemin
49 |
50 |
51 | MateuszKubuszok
52 | Mateusz Kubuszok
53 | http://github.com/MateuszKubuszok
54 |
55 |
56 | )
57 | )
58 |
59 | lazy val noPublishSettings =
60 | Seq(publish := (()), publishLocal := (()), publishArtifact := false)
61 |
--------------------------------------------------------------------------------
/project/Dependencies.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | import Dependencies._
4 |
5 | object Dependencies {
6 |
7 | // scala version
8 | val scalaOrganization = "org.scala-lang"
9 | val scalaVersion = "2.12.6"
10 |
11 | // build tools versions
12 | val scalaFmtVersion = "1.5.0"
13 |
14 | // libraries versions
15 | val swaggerVersion = "1.5.20"
16 |
17 | // resolvers
18 | val resolvers = Seq(
19 | Resolver sonatypeRepo "public",
20 | Resolver typesafeRepo "releases"
21 | )
22 |
23 | // dependencies
24 | val swaggerCore = "io.swagger" % "swagger-core" % swaggerVersion
25 | val swaggerAnnotations = "io.swagger" % "swagger-annotations" % swaggerVersion
26 | val swaggerModels = "io.swagger" % "swagger-models" % swaggerVersion
27 | val swaggerJaxrs = "io.swagger" % "swagger-jaxrs" % swaggerVersion
28 | val swaggerAkkaHttp = "com.github.swagger-akka-http" %% "swagger-akka-http" % "0.14.0"
29 | val classUtil = "org.clapper" %% "classutil" % "1.2.0"
30 | }
31 |
32 | trait Dependencies {
33 |
34 | val scalaOrganizationUsed = scalaOrganization
35 | val scalaVersionUsed = scalaVersion
36 |
37 | val scalaFmtVersionUsed = scalaFmtVersion
38 |
39 | // resolvers
40 | val commonResolvers = resolvers
41 |
42 | val mainDeps = Seq(swaggerAkkaHttp, classUtil)
43 |
44 | val testDeps = Seq()
45 |
46 | implicit class ProjectRoot(project: Project) {
47 |
48 | def root: Project = project in file(".")
49 | }
50 |
51 | implicit class ProjectFrom(project: Project) {
52 |
53 | private val moduleDir = "modules"
54 |
55 | def module(dir: String): Project = project in file(s"$moduleDir/$dir")
56 | }
57 |
58 | implicit class DependsOnProject(project: Project) {
59 |
60 | private val testConfigurations = Set("test", "fun", "it")
61 | private def findCompileAndTestConfigs(p: Project) =
62 | (p.configurations.map(_.name).toSet intersect testConfigurations) + "compile"
63 |
64 | private val thisProjectsConfigs = findCompileAndTestConfigs(project)
65 | private def generateDepsForProject(p: Project) =
66 | p % (thisProjectsConfigs intersect findCompileAndTestConfigs(p) map (c => s"$c->$c") mkString ";")
67 |
68 | def compileAndTestDependsOn(projects: Project*): Project =
69 | project dependsOn (projects.map(generateDepsForProject): _*)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/main/scala/io/scalaland/sbtswagger2/SwaggerOutput.scala:
--------------------------------------------------------------------------------
1 | package io.scalaland.sbtswagger2
2 |
3 | import com.github.swagger.akka.{ SwaggerGenerator, SwaggerScalaModelConverter }
4 | import com.github.swagger.akka.model._
5 | import io.swagger.converter.ModelConverters
6 | import sbt.{ file, File }
7 | import _root_.io.swagger.models.{ Info => _, _ }
8 | import _root_.io.swagger.models.auth._
9 |
10 | object SwaggerOutput {
11 |
12 | class Generator(self: SwaggerOutput, classes: Set[Class[_]]) extends SwaggerGenerator {
13 | ModelConverters.getInstance.addConverter(new SwaggerScalaModelConverter)
14 |
15 | override val apiClasses: Set[Class[_]] = classes.filter(self.inputFilter)
16 | override val host: String = self.host
17 | override val basePath: String = self.basePath
18 | override val info: Info = self.info
19 | override val schemes: List[Scheme] = self.schemes
20 | override val securitySchemeDefinitions: Map[String, SecuritySchemeDefinition] = self.securitySchemeDefinitions
21 | override val externalDocs: Option[ExternalDocs] = self.externalDocs
22 | override val vendorExtensions: Map[String, Object] = self.vendorExtensions
23 | override val unwantedDefinitions: Seq[String] = self.unwantedDefinitions
24 | }
25 | }
26 |
27 | final case class SwaggerOutput(
28 | inputFilter: Class[_] => Boolean = _ => true,
29 | output: File = file("swagger.json"),
30 | host: String = "",
31 | basePath: String = "/",
32 | info: Info = Info(),
33 | schemes: List[Scheme] = List(Scheme.HTTP),
34 | securitySchemeDefinitions: Map[String, SecuritySchemeDefinition] = Map.empty,
35 | externalDocs: Option[ExternalDocs] = None,
36 | vendorExtensions: Map[String, Object] = Map.empty,
37 | unwantedDefinitions: Seq[String] = Seq.empty
38 | ) {
39 |
40 | def generator(classLoader: ClassLoader, classes: Set[Class[_]]): SwaggerGenerator =
41 | classLoader
42 | .loadClass(classOf[SwaggerOutput.Generator].getName)
43 | .getConstructors
44 | .head
45 | .newInstance(this, classes)
46 | .asInstanceOf[SwaggerOutput.Generator]
47 | }
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sbt-swagger-2
2 |
3 | [](https://travis-ci.org/scalalandio/sbt-swagger-2)
4 | [](http://search.maven.org/#search%7Cga%7C1%7Csbt-swagger-2)
5 | [](http://www.apache.org/licenses/LICENSE-2.0.txt)
6 |
7 | > **This project is archived.** I moved away from using `swagger-core` in general, I am **very** happy about it and I cannot support this plugin anymore. If you are interested in using it and you need some modifications, I suggest forking it. Alternatively - if you are able to - move away to [endpoints](https://julienrf.github.io/endpoints/) or [tapir](https://github.com/softwaremill/tapir).
8 |
9 | This plugin is a sbt wrapper for a [Swagger Akka HTTP](https://github.com/swagger-akka-http/swagger-akka-http), that
10 | allows you to generate one (or more) Swagger JSON files during build instead of doing it in runtime.
11 |
12 | As such you can annotate your APIs, the same way you did in Swagger Akka HTTP. Then you can generate `swagger.json` only
13 | after you modified files, remove all dependencies from build, and use `getFromResource("swagger.json")` to serve it.
14 |
15 | ## Motivation
16 |
17 | * I wanted to limit dependencies used in runtime - as I used Circe and Jaws for JSON, shipping separate library
18 | (Jackson) as well as quite a lot other dependencies just for Swagger seems like unnnecessary overhead,
19 | * annotation-based Swagger generators use runtime reflection - for some people that solution itself is a disadvantage,
20 | * when I tried to create a native image in GraalVM, it failed due to usage of reflection - moving Swagger generation
21 | to build stage is a part of the effort of overall removal of runtime reflection in my projects,
22 | * [https://github.com/hootsuite/sbt-swagger](sbt-swagger) is outdated: it isn't migrated to sbt 1.0.0 and uses older
23 | version of Swagger.
24 |
25 | ## Usage
26 |
27 | First add plugin to `project/plugins.sbt` file:
28 |
29 | ```scala
30 | addSbtPlugin("io.scalaland" % "sbt-swagger-2" % sbtSwagger2Version)
31 | ```
32 |
33 | Project basically reuse all constructs from [Swagger Akka HTTP](https://github.com/swagger-akka-http/swagger-akka-http).
34 | It add aliases for them in `io.scalaland.sbtswagger2.SbtSwagger2Plugin.autoImport`, so classes used for creating config
35 | can be found within `Swagger` object.
36 |
37 | ```scala
38 | // API v1
39 | swaggerOutputs += Swagger.Output(
40 | inputFilter = clazz => Set(
41 | "backend.healthcheck",
42 | "backend.auth",
43 | "backend.api.v1"
44 | ).exists(prefix => clazz.getName.startsWith(prefix)),
45 | output = (Compile / classDirectory).value / "docs" / "v1" / "swagger.json",
46 | host = "http://localhost",
47 | schemes = List(Swagger.Scheme.HTTP, Swagger.Scheme.HTTPS),
48 | securitySchemeDefinitions = Map("token" -> new Swagger.OAuth2Definition().password("/auth")),
49 | info = Swagger.Info(
50 | title = "Backend API v1",
51 | version = version.value,
52 | description = """V1 API description""".stripMargin
53 | )
54 | )
55 |
56 | // API v2
57 | swaggerOutputs += Swagger.Output(
58 | inputFilter = clazz => Set(
59 | "backend.healthcheck",
60 | "backend.auth",
61 | "backend.api.v2"
62 | ).exists(prefix => clazz.getName.startsWith(prefix)),
63 | ...
64 | )
65 | ```
66 |
67 | It adds however whilelisting ability, so that one could cherry-pick classes to use in each of (potentially) many files.
68 |
69 | Once configured one can run it with:
70 |
71 | ```scala
72 | sbt> swaggerGenerate
73 | ```
74 |
75 | ## Requirements and limitations
76 |
77 | This plugin requires sbt 1.1.0+.
78 |
79 | So far I haven't found a way to run generator automatically after each compilation without having issue with circular
80 | depenendencies and sbt freeze, so you should select resources dir as the target and run `swaggerGenerate` manually.
81 |
--------------------------------------------------------------------------------
/modules/sbt-swagger-2/src/main/scala/io/scalaland/sbtswagger2/SbtSwagger2Plugin.scala:
--------------------------------------------------------------------------------
1 | package io.scalaland.sbtswagger2
2 |
3 | import io.swagger.annotations
4 | import org.clapper.classutil.{ ClassFinder, ClassUtil }
5 | import sbt._
6 | import sbt.Keys._
7 | import sbt.internal.PluginManagement.PluginClassLoader
8 | import sbt.plugins.JvmPlugin
9 |
10 | import scala.collection.immutable.ListSet
11 |
12 | object SbtSwagger2Plugin extends AutoPlugin {
13 |
14 | override def trigger = allRequirements
15 | override def requires = JvmPlugin
16 |
17 | object autoImport {
18 |
19 | val swaggerVersion = settingKey[String]("Version of Swagger Annotations that should be added to project")
20 | val swaggerJsr311Version = settingKey[String]("Version of JSR311 that should be added to project")
21 | val swaggerOrdering = settingKey[Ordering[Class[_]]]("How endpoints should be ordered in file")
22 | val swaggerOutputs = settingKey[Seq[SwaggerOutput]]("Configurations of all intended Swagger outputs")
23 | val swaggerGenerate = taskKey[Seq[File]]("Generate Swagger outputs")
24 |
25 | object Swagger {
26 |
27 | type Output = _root_.io.scalaland.sbtswagger2.SwaggerOutput
28 | val Output = _root_.io.scalaland.sbtswagger2.SwaggerOutput
29 |
30 | type Contact = _root_.com.github.swagger.akka.model.Contact
31 | val Contact = _root_.com.github.swagger.akka.model.Contact
32 |
33 | type ExternalDocs = _root_.io.swagger.models.ExternalDocs
34 |
35 | type Info = _root_.com.github.swagger.akka.model.Info
36 | val Info = _root_.com.github.swagger.akka.model.Info
37 |
38 | type License = _root_.com.github.swagger.akka.model.License
39 | val License = _root_.com.github.swagger.akka.model.License
40 |
41 | type Scheme = _root_.io.swagger.models.Scheme
42 | object Scheme {
43 | val HTTP = _root_.io.swagger.models.Scheme.HTTP
44 | val HTTPS = _root_.io.swagger.models.Scheme.HTTPS
45 | val WS = _root_.io.swagger.models.Scheme.WS
46 | val WSS = _root_.io.swagger.models.Scheme.WSS
47 | }
48 |
49 | type SecuritySchemeDefinition = _root_.io.swagger.models.auth.SecuritySchemeDefinition
50 | type ApiKeyAuthDefinition = _root_.io.swagger.models.auth.ApiKeyAuthDefinition
51 | type BasicAuthDefinition = _root_.io.swagger.models.auth.BasicAuthDefinition
52 | type OAuth2Definition = _root_.io.swagger.models.auth.OAuth2Definition
53 | }
54 | }
55 |
56 | import autoImport._
57 |
58 | override lazy val projectSettings = Seq(
59 | swaggerVersion := "1.5.20",
60 | swaggerJsr311Version := "1.1.1",
61 | swaggerOrdering := Ordering.by[Class[_], String](_.getName),
62 | swaggerOutputs := Seq.empty,
63 | swaggerGenerate := {
64 | implicit val ordering = swaggerOrdering.value
65 | val classPath = (fullClasspath in Compile).value
66 | val inputDirectories = classPath.files.filter(_.getName.endsWith("classes")) :+ (classDirectory in Runtime).value
67 | val log = streams.value.log
68 | val apiClass = classOf[annotations.Api]
69 | val apiSignature = ClassUtil.classSignature(apiClass)
70 |
71 | val pluginClassLoader = apiClass.getClassLoader.asInstanceOf[PluginClassLoader]
72 | pluginClassLoader.add(classPath.files.map(_.toURI.toURL))
73 | log.info(s"Looking for compiled project classes in ${inputDirectories.map(_.toURI.toURL).mkString(", ")} ...")
74 |
75 | val classes = ClassFinder(inputDirectories).getClasses
76 | .filter(_.annotations.exists(_.descriptor == apiSignature))
77 | .map { classInfo =>
78 | Class.forName(classInfo.name, false, pluginClassLoader)
79 | }
80 | .sorted
81 | .to[ListSet]
82 |
83 | val output = swaggerOutputs.value.map { swaggerConfig =>
84 | log.info(s"Generating Swagger JSON in ${swaggerConfig.output} ...")
85 | IO.write(swaggerConfig.output, swaggerConfig.generator(pluginClassLoader, classes).generateSwaggerJson)
86 | swaggerConfig.output
87 | }
88 | log.info(s"Done generating ${output.size} Swagger JSON.")
89 | output
90 | },
91 | libraryDependencies += "io.swagger" % "swagger-annotations" % swaggerVersion.value,
92 | libraryDependencies += "javax.ws.rs" % "jsr311-api" % swaggerJsr311Version.value
93 | )
94 |
95 | override lazy val buildSettings = Seq()
96 |
97 | override lazy val globalSettings = Seq()
98 | }
99 |
--------------------------------------------------------------------------------
/project/Settings.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import sbt.Keys._
3 | import com.lucidchart.sbt.scalafmt.ScalafmtCorePlugin.autoImport._
4 | import com.typesafe.sbt._
5 | import org.scalastyle.sbt.ScalastylePlugin.autoImport._
6 | import wartremover._
7 |
8 | object Settings extends Dependencies {
9 |
10 | private val commonSettings = Seq(
11 | organization := "io.scalaland",
12 |
13 | scalaOrganization := scalaOrganizationUsed,
14 | scalaVersion := scalaVersionUsed,
15 |
16 | scalafmtVersion := scalaFmtVersionUsed
17 | )
18 |
19 | private val rootSettings = commonSettings
20 |
21 | private val modulesSettings = commonSettings ++ Seq(
22 | scalacOptions ++= Seq(
23 | // standard settings
24 | "-target:jvm-1.8",
25 | "-encoding", "UTF-8",
26 | "-unchecked",
27 | "-deprecation",
28 | "-explaintypes",
29 | "-feature",
30 | // language features
31 | "-language:existentials",
32 | "-language:higherKinds",
33 | "-language:implicitConversions",
34 | "-language:postfixOps",
35 | // private options
36 | // "-Yinduction-heuristics",
37 | "-Yno-adapted-args",
38 | "-Ypartial-unification",
39 | // "-Yno-imports",
40 | // "-Ysysdef", "io.skillhouse.Predef._", // backed off until it is a stable feature
41 | // "-Ypredef", "_", // backed off until it is a stable feature
42 | // warnings
43 | "-Ywarn-dead-code",
44 | "-Ywarn-extra-implicit",
45 | "-Ywarn-inaccessible",
46 | "-Ywarn-infer-any",
47 | "-Ywarn-macros:after",
48 | "-Ywarn-nullary-override",
49 | "-Ywarn-nullary-unit",
50 | "-Ywarn-numeric-widen",
51 | "-Ywarn-unused:implicits",
52 | "-Ywarn-unused:imports",
53 | "-Ywarn-unused:locals",
54 | "-Ywarn-unused:params",
55 | "-Ywarn-unused:patvars",
56 | "-Ywarn-unused:privates",
57 | "-Ywarn-value-discard",
58 | // advanced options
59 | "-Xcheckinit",
60 | "-Xfatal-warnings",
61 | "-Xfuture",
62 | // "-Xstrict-patmat-analysis",
63 | // linting
64 | "-Xlint",
65 | "-Xlint:adapted-args",
66 | "-Xlint:by-name-right-associative",
67 | "-Xlint:constant",
68 | "-Xlint:delayedinit-select",
69 | "-Xlint:doc-detached",
70 | "-Xlint:inaccessible",
71 | "-Xlint:infer-any",
72 | "-Xlint:missing-interpolator",
73 | "-Xlint:nullary-override",
74 | "-Xlint:nullary-unit",
75 | "-Xlint:option-implicit",
76 | "-Xlint:package-object-classes",
77 | "-Xlint:poly-implicit-overload",
78 | "-Xlint:private-shadow",
79 | "-Xlint:stars-align",
80 | // "-Xlint:strict-unsealed-patmat",
81 | "-Xlint:type-parameter-shadow",
82 | "-Xlint:unsound-match"
83 | ),
84 | Compile / console / scalacOptions --= Seq(
85 | // warnings
86 | "-Ywarn-unused:implicits",
87 | "-Ywarn-unused:imports",
88 | "-Ywarn-unused:locals",
89 | "-Ywarn-unused:params",
90 | "-Ywarn-unused:patvars",
91 | "-Ywarn-unused:privates",
92 | // advanced options
93 | "-Xfatal-warnings",
94 | // linting
95 | "-Xlint"
96 | ),
97 |
98 | resolvers ++= commonResolvers,
99 |
100 | libraryDependencies ++= mainDeps,
101 |
102 | Compile / scalafmtOnCompile := true,
103 |
104 | scalastyleFailOnError := true,
105 |
106 | Compile / compile / wartremoverWarnings ++= Warts.allBut(
107 | Wart.Any,
108 | Wart.AsInstanceOf,
109 | Wart.DefaultArguments,
110 | Wart.ExplicitImplicitTypes,
111 | Wart.ImplicitConversion,
112 | Wart.ImplicitParameter,
113 | Wart.Overloading,
114 | Wart.PublicInference,
115 | Wart.NonUnitStatements,
116 | Wart.Nothing,
117 | Wart.Serializable
118 | )
119 | )
120 |
121 | implicit class DataConfigurator(project: Project) {
122 |
123 | def setName(newName: String): Project = project.settings(name := newName)
124 |
125 | def setDescription(newDescription: String): Project = project.settings(description := newDescription)
126 |
127 | def setInitialCommand(newInitialCommand: String): Project =
128 | project.settings(initialCommands := s"import io.scalaland.sbtswagger2._, $newInitialCommand")
129 | }
130 |
131 | implicit class RootConfigurator(project: Project) {
132 |
133 | def configureRoot: Project = project.settings(rootSettings: _*)
134 | }
135 |
136 | implicit class ModuleConfigurator(project: Project) {
137 |
138 | def configureModule: Project = project.settings(modulesSettings: _*).enablePlugins(GitVersioning)
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/sbt:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # A more capable sbt runner, coincidentally also called sbt.
4 | # Author: Paul Phillips
5 | # https://github.com/paulp/sbt-extras
6 |
7 | set -o pipefail
8 |
9 | declare -r sbt_release_version="1.2.8"
10 | declare -r sbt_unreleased_version="1.3.0-RC1"
11 |
12 | declare -r latest_213="2.13.0"
13 | declare -r latest_212="2.12.9"
14 | declare -r latest_211="2.11.12"
15 | declare -r latest_210="2.10.7"
16 | declare -r latest_29="2.9.3"
17 | declare -r latest_28="2.8.2"
18 |
19 | declare -r buildProps="project/build.properties"
20 |
21 | declare -r sbt_launch_ivy_release_repo="https://repo.typesafe.com/typesafe/ivy-releases"
22 | declare -r sbt_launch_ivy_snapshot_repo="https://repo.scala-sbt.org/scalasbt/ivy-snapshots"
23 | declare -r sbt_launch_mvn_release_repo="https://repo.scala-sbt.org/scalasbt/maven-releases"
24 | declare -r sbt_launch_mvn_snapshot_repo="https://repo.scala-sbt.org/scalasbt/maven-snapshots"
25 |
26 | declare -r default_jvm_opts_common="-Xms512m -Xss2m"
27 | declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy"
28 |
29 | declare sbt_jar sbt_dir sbt_create sbt_version sbt_script sbt_new
30 | declare sbt_explicit_version
31 | declare verbose noshare batch trace_level
32 | declare debugUs
33 |
34 | declare java_cmd="java"
35 | declare sbt_launch_dir="$HOME/.sbt/launchers"
36 | declare sbt_launch_repo
37 |
38 | # pull -J and -D options to give to java.
39 | declare -a java_args scalac_args sbt_commands residual_args
40 |
41 | # args to jvm/sbt via files or environment variables
42 | declare -a extra_jvm_opts extra_sbt_opts
43 |
44 | echoerr () { echo >&2 "$@"; }
45 | vlog () { [[ -n "$verbose" ]] && echoerr "$@"; }
46 | die () { echo "Aborting: $*" ; exit 1; }
47 |
48 | setTrapExit () {
49 | # save stty and trap exit, to ensure echo is re-enabled if we are interrupted.
50 | SBT_STTY="$(stty -g 2>/dev/null)"
51 | export SBT_STTY
52 |
53 | # restore stty settings (echo in particular)
54 | onSbtRunnerExit() {
55 | [ -t 0 ] || return
56 | vlog ""
57 | vlog "restoring stty: $SBT_STTY"
58 | stty "$SBT_STTY"
59 | }
60 |
61 | vlog "saving stty: $SBT_STTY"
62 | trap onSbtRunnerExit EXIT
63 | }
64 |
65 | # this seems to cover the bases on OSX, and someone will
66 | # have to tell me about the others.
67 | get_script_path () {
68 | local path="$1"
69 | [[ -L "$path" ]] || { echo "$path" ; return; }
70 |
71 | local -r target="$(readlink "$path")"
72 | if [[ "${target:0:1}" == "/" ]]; then
73 | echo "$target"
74 | else
75 | echo "${path%/*}/$target"
76 | fi
77 | }
78 |
79 | script_path="$(get_script_path "${BASH_SOURCE[0]}")"
80 | declare -r script_path
81 | script_name="${script_path##*/}"
82 | declare -r script_name
83 |
84 | init_default_option_file () {
85 | local overriding_var="${!1}"
86 | local default_file="$2"
87 | if [[ ! -r "$default_file" && "$overriding_var" =~ ^@(.*)$ ]]; then
88 | local envvar_file="${BASH_REMATCH[1]}"
89 | if [[ -r "$envvar_file" ]]; then
90 | default_file="$envvar_file"
91 | fi
92 | fi
93 | echo "$default_file"
94 | }
95 |
96 | sbt_opts_file="$(init_default_option_file SBT_OPTS .sbtopts)"
97 | jvm_opts_file="$(init_default_option_file JVM_OPTS .jvmopts)"
98 |
99 | build_props_sbt () {
100 | [[ -r "$buildProps" ]] && \
101 | grep '^sbt\.version' "$buildProps" | tr '=\r' ' ' | awk '{ print $2; }'
102 | }
103 |
104 | set_sbt_version () {
105 | sbt_version="${sbt_explicit_version:-$(build_props_sbt)}"
106 | [[ -n "$sbt_version" ]] || sbt_version=$sbt_release_version
107 | export sbt_version
108 | }
109 |
110 | url_base () {
111 | local version="$1"
112 |
113 | case "$version" in
114 | 0.7.*) echo "https://simple-build-tool.googlecode.com" ;;
115 | 0.10.* ) echo "$sbt_launch_ivy_release_repo" ;;
116 | 0.11.[12]) echo "$sbt_launch_ivy_release_repo" ;;
117 | 0.*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmdd-hhMMss"
118 | echo "$sbt_launch_ivy_snapshot_repo" ;;
119 | 0.*) echo "$sbt_launch_ivy_release_repo" ;;
120 | *-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]) # ie "*-yyyymmdd-hhMMss"
121 | echo "$sbt_launch_mvn_snapshot_repo" ;;
122 | *) echo "$sbt_launch_mvn_release_repo" ;;
123 | esac
124 | }
125 |
126 | make_url () {
127 | local version="$1"
128 |
129 | local base="${sbt_launch_repo:-$(url_base "$version")}"
130 |
131 | case "$version" in
132 | 0.7.*) echo "$base/files/sbt-launch-0.7.7.jar" ;;
133 | 0.10.* ) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;;
134 | 0.11.[12]) echo "$base/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;;
135 | 0.*) echo "$base/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" ;;
136 | *) echo "$base/org/scala-sbt/sbt-launch/$version/sbt-launch-${version}.jar" ;;
137 | esac
138 | }
139 |
140 | addJava () { vlog "[addJava] arg = '$1'" ; java_args+=("$1"); }
141 | addSbt () { vlog "[addSbt] arg = '$1'" ; sbt_commands+=("$1"); }
142 | addScalac () { vlog "[addScalac] arg = '$1'" ; scalac_args+=("$1"); }
143 | addResidual () { vlog "[residual] arg = '$1'" ; residual_args+=("$1"); }
144 |
145 | addResolver () { addSbt "set resolvers += $1"; }
146 | addDebugger () { addJava "-Xdebug" ; addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"; }
147 | setThisBuild () {
148 | vlog "[addBuild] args = '$*'"
149 | local key="$1" && shift
150 | addSbt "set $key in ThisBuild := $*"
151 | }
152 | setScalaVersion () {
153 | [[ "$1" == *"-SNAPSHOT" ]] && addResolver 'Resolver.sonatypeRepo("snapshots")'
154 | addSbt "++ $1"
155 | }
156 | setJavaHome () {
157 | java_cmd="$1/bin/java"
158 | setThisBuild javaHome "_root_.scala.Some(file(\"$1\"))"
159 | export JAVA_HOME="$1"
160 | export JDK_HOME="$1"
161 | export PATH="$JAVA_HOME/bin:$PATH"
162 | }
163 |
164 | getJavaVersion() {
165 | local -r str=$("$1" -version 2>&1 | grep -E -e '(java|openjdk) version' | awk '{ print $3 }' | tr -d '"')
166 |
167 | # java -version on java8 says 1.8.x
168 | # but on 9 and 10 it's 9.x.y and 10.x.y.
169 | if [[ "$str" =~ ^1\.([0-9]+)\..*$ ]]; then
170 | echo "${BASH_REMATCH[1]}"
171 | elif [[ "$str" =~ ^([0-9]+)\..*$ ]]; then
172 | echo "${BASH_REMATCH[1]}"
173 | elif [[ -n "$str" ]]; then
174 | echoerr "Can't parse java version from: $str"
175 | fi
176 | }
177 |
178 | checkJava() {
179 | # Warn if there is a Java version mismatch between PATH and JAVA_HOME/JDK_HOME
180 |
181 | [[ -n "$JAVA_HOME" && -e "$JAVA_HOME/bin/java" ]] && java="$JAVA_HOME/bin/java"
182 | [[ -n "$JDK_HOME" && -e "$JDK_HOME/lib/tools.jar" ]] && java="$JDK_HOME/bin/java"
183 |
184 | if [[ -n "$java" ]]; then
185 | pathJavaVersion=$(getJavaVersion java)
186 | homeJavaVersion=$(getJavaVersion "$java")
187 | if [[ "$pathJavaVersion" != "$homeJavaVersion" ]]; then
188 | echoerr "Warning: Java version mismatch between PATH and JAVA_HOME/JDK_HOME, sbt will use the one in PATH"
189 | echoerr " Either: fix your PATH, remove JAVA_HOME/JDK_HOME or use -java-home"
190 | echoerr " java version from PATH: $pathJavaVersion"
191 | echoerr " java version from JAVA_HOME/JDK_HOME: $homeJavaVersion"
192 | fi
193 | fi
194 | }
195 |
196 | java_version () {
197 | local -r version=$(getJavaVersion "$java_cmd")
198 | vlog "Detected Java version: $version"
199 | echo "$version"
200 | }
201 |
202 | # MaxPermSize critical on pre-8 JVMs but incurs noisy warning on 8+
203 | default_jvm_opts () {
204 | local -r v="$(java_version)"
205 | if [[ $v -ge 8 ]]; then
206 | echo "$default_jvm_opts_common"
207 | else
208 | echo "-XX:MaxPermSize=384m $default_jvm_opts_common"
209 | fi
210 | }
211 |
212 | build_props_scala () {
213 | if [[ -r "$buildProps" ]]; then
214 | versionLine="$(grep '^build.scala.versions' "$buildProps")"
215 | versionString="${versionLine##build.scala.versions=}"
216 | echo "${versionString%% .*}"
217 | fi
218 | }
219 |
220 | execRunner () {
221 | # print the arguments one to a line, quoting any containing spaces
222 | vlog "# Executing command line:" && {
223 | for arg; do
224 | if [[ -n "$arg" ]]; then
225 | if printf "%s\n" "$arg" | grep -q ' '; then
226 | printf >&2 "\"%s\"\n" "$arg"
227 | else
228 | printf >&2 "%s\n" "$arg"
229 | fi
230 | fi
231 | done
232 | vlog ""
233 | }
234 |
235 | setTrapExit
236 |
237 | if [[ -n "$batch" ]]; then
238 | "$@" < /dev/null
239 | else
240 | "$@"
241 | fi
242 | }
243 |
244 | jar_url () { make_url "$1"; }
245 |
246 | is_cygwin () { [[ "$(uname -a)" == "CYGWIN"* ]]; }
247 |
248 | jar_file () {
249 | is_cygwin \
250 | && cygpath -w "$sbt_launch_dir/$1/sbt-launch.jar" \
251 | || echo "$sbt_launch_dir/$1/sbt-launch.jar"
252 | }
253 |
254 | download_url () {
255 | local url="$1"
256 | local jar="$2"
257 |
258 | mkdir -p "${jar%/*}" && {
259 | if command -v curl > /dev/null 2>&1; then
260 | curl --fail --silent --location "$url" --output "$jar"
261 | elif command -v wget > /dev/null 2>&1; then
262 | wget -q -O "$jar" "$url"
263 | fi
264 | } && [[ -r "$jar" ]]
265 | }
266 |
267 | acquire_sbt_jar () {
268 | {
269 | sbt_jar="$(jar_file "$sbt_version")"
270 | [[ -r "$sbt_jar" ]]
271 | } || {
272 | sbt_jar="$HOME/.ivy2/local/org.scala-sbt/sbt-launch/$sbt_version/jars/sbt-launch.jar"
273 | [[ -r "$sbt_jar" ]]
274 | } || {
275 | sbt_jar="$(jar_file "$sbt_version")"
276 | jar_url="$(make_url "$sbt_version")"
277 |
278 | echoerr "Downloading sbt launcher for ${sbt_version}:"
279 | echoerr " From ${jar_url}"
280 | echoerr " To ${sbt_jar}"
281 |
282 | download_url "${jar_url}" "${sbt_jar}"
283 |
284 | case "${sbt_version}" in
285 | 0.*) vlog "SBT versions < 1.0 do not have published MD5 checksums, skipping check"; echo "" ;;
286 | *) verify_sbt_jar "${sbt_jar}" ;;
287 | esac
288 | }
289 | }
290 |
291 | verify_sbt_jar() {
292 | local jar="${1}"
293 | local md5="${jar}.md5"
294 |
295 | download_url "$(make_url "${sbt_version}").md5" "${md5}" > /dev/null 2>&1
296 |
297 | if command -v md5sum > /dev/null 2>&1; then
298 | if echo "$(cat "${md5}") ${jar}" | md5sum -c -; then
299 | rm -rf "${md5}"
300 | return 0
301 | else
302 | echoerr "Checksum does not match"
303 | return 1
304 | fi
305 | elif command -v md5 > /dev/null 2>&1; then
306 | if [ "$(md5 -q "${jar}")" == "$(cat "${md5}")" ]; then
307 | rm -rf "${md5}"
308 | return 0
309 | else
310 | echoerr "Checksum does not match"
311 | return 1
312 | fi
313 | elif command -v openssl > /dev/null 2>&1; then
314 | if [ "$(openssl md5 -r "${jar}" | awk '{print $1}')" == "$(cat "${md5}")" ]; then
315 | rm -rf "${md5}"
316 | return 0
317 | else
318 | echoerr "Checksum does not match"
319 | return 1
320 | fi
321 | else
322 | echoerr "Could not find an MD5 command"
323 | return 1
324 | fi
325 | }
326 |
327 | usage () {
328 | set_sbt_version
329 | cat < display stack traces with a max of frames (default: -1, traces suppressed)
348 | -debug-inc enable debugging log for the incremental compiler
349 | -no-colors disable ANSI color codes
350 | -sbt-create start sbt even if current directory contains no sbt project
351 | -sbt-dir path to global settings/plugins directory (default: ~/.sbt/)
352 | -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11+)
353 | -ivy path to local Ivy repository (default: ~/.ivy2)
354 | -no-share use all local caches; no sharing
355 | -offline put sbt in offline mode
356 | -jvm-debug Turn on JVM debugging, open at the given port.
357 | -batch Disable interactive mode
358 | -prompt Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted
359 | -script Run the specified file as a scala script
360 |
361 | # sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version)
362 | -sbt-force-latest force the use of the latest release of sbt: $sbt_release_version
363 | -sbt-version use the specified version of sbt (default: $sbt_release_version)
364 | -sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version
365 | -sbt-jar use the specified jar as the sbt launcher
366 | -sbt-launch-dir directory to hold sbt launchers (default: $sbt_launch_dir)
367 | -sbt-launch-repo repo url for downloading sbt launcher jar (default: $(url_base "$sbt_version"))
368 |
369 | # scala version (default: as chosen by sbt)
370 | -28 use $latest_28
371 | -29 use $latest_29
372 | -210 use $latest_210
373 | -211 use $latest_211
374 | -212 use $latest_212
375 | -213 use $latest_213
376 | -scala-home use the scala build at the specified directory
377 | -scala-version use the specified version of scala
378 | -binary-version use the specified scala version when searching for dependencies
379 |
380 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
381 | -java-home alternate JAVA_HOME
382 |
383 | # passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution
384 | # The default set is used if JVM_OPTS is unset and no -jvm-opts file is found
385 | $(default_jvm_opts)
386 | JVM_OPTS environment variable holding either the jvm args directly, or
387 | the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts')
388 | Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument.
389 | -jvm-opts file containing jvm args (if not given, .jvmopts in project root is used if present)
390 | -Dkey=val pass -Dkey=val directly to the jvm
391 | -J-X pass option -X directly to the jvm (-J is stripped)
392 |
393 | # passing options to sbt, OR to this runner
394 | SBT_OPTS environment variable holding either the sbt args directly, or
395 | the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts')
396 | Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument.
397 | -sbt-opts file containing sbt args (if not given, .sbtopts in project root is used if present)
398 | -S-X add -X to sbt's scalacOptions (-S is stripped)
399 | EOM
400 | }
401 |
402 | process_args () {
403 | require_arg () {
404 | local type="$1"
405 | local opt="$2"
406 | local arg="$3"
407 |
408 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
409 | die "$opt requires <$type> argument"
410 | fi
411 | }
412 | while [[ $# -gt 0 ]]; do
413 | case "$1" in
414 | -h|-help) usage; exit 0 ;;
415 | -v) verbose=true && shift ;;
416 | -d) addSbt "--debug" && shift ;;
417 | -w) addSbt "--warn" && shift ;;
418 | -q) addSbt "--error" && shift ;;
419 | -x) debugUs=true && shift ;;
420 | -trace) require_arg integer "$1" "$2" && trace_level="$2" && shift 2 ;;
421 | -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;;
422 | -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;;
423 | -no-share) noshare=true && shift ;;
424 | -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;;
425 | -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;;
426 | -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;;
427 | -offline) addSbt "set offline in Global := true" && shift ;;
428 | -jvm-debug) require_arg port "$1" "$2" && addDebugger "$2" && shift 2 ;;
429 | -batch) batch=true && shift ;;
430 | -prompt) require_arg "expr" "$1" "$2" && setThisBuild shellPrompt "(s => { val e = Project.extract(s) ; $2 })" && shift 2 ;;
431 | -script) require_arg file "$1" "$2" && sbt_script="$2" && addJava "-Dsbt.main.class=sbt.ScriptMain" && shift 2 ;;
432 |
433 | -sbt-create) sbt_create=true && shift ;;
434 | -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;;
435 | -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;;
436 | -sbt-force-latest) sbt_explicit_version="$sbt_release_version" && shift ;;
437 | -sbt-dev) sbt_explicit_version="$sbt_unreleased_version" && shift ;;
438 | -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;;
439 | -sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;;
440 | -scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;;
441 | -binary-version) require_arg version "$1" "$2" && setThisBuild scalaBinaryVersion "\"$2\"" && shift 2 ;;
442 | -scala-home) require_arg path "$1" "$2" && setThisBuild scalaHome "_root_.scala.Some(file(\"$2\"))" && shift 2 ;;
443 | -java-home) require_arg path "$1" "$2" && setJavaHome "$2" && shift 2 ;;
444 | -sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;;
445 | -jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;;
446 |
447 | -D*) addJava "$1" && shift ;;
448 | -J*) addJava "${1:2}" && shift ;;
449 | -S*) addScalac "${1:2}" && shift ;;
450 | -28) setScalaVersion "$latest_28" && shift ;;
451 | -29) setScalaVersion "$latest_29" && shift ;;
452 | -210) setScalaVersion "$latest_210" && shift ;;
453 | -211) setScalaVersion "$latest_211" && shift ;;
454 | -212) setScalaVersion "$latest_212" && shift ;;
455 | -213) setScalaVersion "$latest_213" && shift ;;
456 | new) sbt_new=true && : ${sbt_explicit_version:=$sbt_release_version} && addResidual "$1" && shift ;;
457 | *) addResidual "$1" && shift ;;
458 | esac
459 | done
460 | }
461 |
462 | # process the direct command line arguments
463 | process_args "$@"
464 |
465 | # skip #-styled comments and blank lines
466 | readConfigFile() {
467 | local end=false
468 | until $end; do
469 | read -r || end=true
470 | [[ $REPLY =~ ^# ]] || [[ -z $REPLY ]] || echo "$REPLY"
471 | done < "$1"
472 | }
473 |
474 | # if there are file/environment sbt_opts, process again so we
475 | # can supply args to this runner
476 | if [[ -r "$sbt_opts_file" ]]; then
477 | vlog "Using sbt options defined in file $sbt_opts_file"
478 | while read -r opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbt_opts_file")
479 | elif [[ -n "$SBT_OPTS" && ! ("$SBT_OPTS" =~ ^@.*) ]]; then
480 | vlog "Using sbt options defined in variable \$SBT_OPTS"
481 | IFS=" " read -r -a extra_sbt_opts <<< "$SBT_OPTS"
482 | else
483 | vlog "No extra sbt options have been defined"
484 | fi
485 |
486 | [[ -n "${extra_sbt_opts[*]}" ]] && process_args "${extra_sbt_opts[@]}"
487 |
488 | # reset "$@" to the residual args
489 | set -- "${residual_args[@]}"
490 | argumentCount=$#
491 |
492 | # set sbt version
493 | set_sbt_version
494 |
495 | checkJava
496 |
497 | # only exists in 0.12+
498 | setTraceLevel() {
499 | case "$sbt_version" in
500 | "0.7."* | "0.10."* | "0.11."* ) echoerr "Cannot set trace level in sbt version $sbt_version" ;;
501 | *) setThisBuild traceLevel "$trace_level" ;;
502 | esac
503 | }
504 |
505 | # set scalacOptions if we were given any -S opts
506 | [[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[*]}\""
507 |
508 | [[ -n "$sbt_explicit_version" && -z "$sbt_new" ]] && addJava "-Dsbt.version=$sbt_explicit_version"
509 | vlog "Detected sbt version $sbt_version"
510 |
511 | if [[ -n "$sbt_script" ]]; then
512 | residual_args=( "$sbt_script" "${residual_args[@]}" )
513 | else
514 | # no args - alert them there's stuff in here
515 | (( argumentCount > 0 )) || {
516 | vlog "Starting $script_name: invoke with -help for other options"
517 | residual_args=( shell )
518 | }
519 | fi
520 |
521 | # verify this is an sbt dir, -create was given or user attempts to run a scala script
522 | [[ -r ./build.sbt || -d ./project || -n "$sbt_create" || -n "$sbt_script" || -n "$sbt_new" ]] || {
523 | cat <