Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add cross-build for Scala 3.0 #290

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 78 additions & 33 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ val jsr305Lib = "com.google.code.findbugs" % "jsr305" % "2.0.1"
val scalacheckLib = "org.scalacheck" %% "scalacheck" % "1.15.4" % "test"
val slf4jApi = "org.slf4j" % "slf4j-api" % slf4jVersion

def scalatestLib(scalaBinaryVersion: String) =
if (isScala3(scalaBinaryVersion))
"org.scalatest" %% "scalatest" % "3.2.9" % "test"
else
"org.scalatest" %% "scalatest" % "3.1.2" % "test"

def scalatestplusScalacheckLib(scalaBinaryVersion: String) =
if (isScala3(scalaBinaryVersion))
"org.scalatestplus" %% "scalacheck-1-15" % "3.2.9.0" % "test"
else
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test"

def scalatestplusJUnitLib(scalaBinaryVersion: String) =
if (isScala3(scalaBinaryVersion))
"org.scalatestplus" %% "junit-4-13" % "3.2.9.0" % "test"
else
"org.scalatestplus" %% "junit-4-12" % "3.1.2.0" % "test"

def scalatestplusMockitoLib(scalaBinaryVersion: String) =
if (isScala3(scalaBinaryVersion))
"org.scalatestplus" %% "mockito-3-4" % "3.2.9.0" % "test"
else
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"

def travisTestJavaOptions: Seq[String] = {
// We have some custom configuration for the Travis environment
// https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
Expand Down Expand Up @@ -82,27 +106,31 @@ def jdk11GcJavaOptions: Seq[String] = {

val defaultProjectSettings = Seq(
scalaVersion := "2.13.6",
crossScalaVersions := Seq("2.12.12", "2.13.6")
crossScalaVersions := Seq("2.12.12", "2.13.6", "3.0.2-RC1") // TODO Scala3 use 3.0.2 once released
)

def isScala3(scalaBinaryVersion: String): Boolean =
scalaBinaryVersion == "3"

val baseSettings = Seq(
version := releaseVersion,
organization := "com.twitter",
// Workaround for a scaladoc bug which causes it to choke on empty classpaths.
Compile / unmanagedClasspath += Attributed.blank(new java.io.File("doesnotexist")),
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-collection-compat" % "2.1.2",
"org.scala-lang.modules" %% "scala-collection-compat" % "2.4.4",
// See https://www.scala-sbt.org/0.13/docs/Testing.html#JUnit
"com.novocode" % "junit-interface" % "0.11" % "test",
"org.scalatest" %% "scalatest" % "3.1.2" % "test",
"org.scalatestplus" %% "junit-4-12" % "3.1.2.0" % "test"
scalatestLib(scalaBinaryVersion.value),
scalatestplusJUnitLib(scalaBinaryVersion.value),
),
Test / fork := true, // We have to fork to get the JavaOptions
// Workaround for cross building HealthyQueue.scala, which is not compatible between
// 2.12- with 2.13+.
Compile / unmanagedSourceDirectories += {
val sourceDir = (Compile / sourceDirectory).value
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, _)) => sourceDir / "scala-2.13+"
case Some((2, n)) if n >= 13 => sourceDir / "scala-2.13+"
case _ => sourceDir / "scala-2.12-"
}
Expand All @@ -120,7 +148,12 @@ val baseSettings = Seq(
// Needs -missing-interpolator due to https://issues.scala-lang.org/browse/SI-8761
"-Xlint:-missing-interpolator",
"-Yrangepos"
),
) ++ Seq(
"-source:3.0-migration",
//"-rewrite",
"-explain",
"-explain-types",
).filter(_ => isScala3(scalaBinaryVersion.value)),
// Note: Use -Xlint rather than -Xlint:unchecked when TestThriftStructure
// warnings are resolved
javacOptions ++= Seq("-Xlint:unchecked", "-source", "1.8", "-target", "1.8"),
Expand Down Expand Up @@ -206,7 +239,7 @@ lazy val util = Project(
utilJvm,
utilLint,
utilLogging,
utilMock,
//utilMock, // TODO Scala3
utilReflect,
utilRegistry,
utilRouting,
Expand All @@ -232,7 +265,7 @@ lazy val utilApp = Project(
name := "util-app",
libraryDependencies ++= Seq(
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"
("org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilAppLifecycle, utilCore, utilRegistry)

Expand Down Expand Up @@ -275,7 +308,7 @@ lazy val utilCache = Project(
caffeineLib,
jsr305Lib,
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"
("org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilCore)

Expand Down Expand Up @@ -318,13 +351,12 @@ lazy val utilCore = Project(
// so that util-core would work better for Pants projects in IntelliJ.
Compile / unmanagedSourceDirectories += baseDirectory.value / "concurrent-extra",
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parser-combinators" % "2.0.0",
caffeineLib % "test",
scalacheckLib,
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2",
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test",
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test"
scalatestplusScalacheckLib(scalaBinaryVersion.value),
scalatestplusMockitoLib(scalaBinaryVersion.value),
),
Compile / resourceGenerators += Def.task {
val projectName = name.value
Expand Down Expand Up @@ -371,7 +403,10 @@ lazy val utilReflect = Project(
).settings(
sharedSettings
).settings(
name := "util-reflect"
name := "util-reflect",
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value
)
).dependsOn(utilCore)

lazy val utilHashing = Project(
Expand All @@ -384,7 +419,7 @@ lazy val utilHashing = Project(
libraryDependencies ++= Seq(
scalacheckLib,
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test"
)
).map(_.cross(CrossVersion.for3Use2_13))
).dependsOn(utilCore % "test")

lazy val utilJacksonAnnotations = Project(
Expand Down Expand Up @@ -427,7 +462,7 @@ lazy val utilJvm = Project(
name := "util-jvm",
libraryDependencies ++= Seq(
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"
("org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilApp, utilCore, utilStats)

Expand Down Expand Up @@ -460,7 +495,7 @@ lazy val utilMock = Project(
// only depend on mockito-scala; it will pull in the correct corresponding version of mockito.
"org.mockito" %% "mockito-scala" % mockitoScalaVersion,
"org.mockito" %% "mockito-scala-scalatest" % mockitoScalaVersion % "test"
)
).map(_.cross(CrossVersion.for3Use2_13))
).dependsOn(utilCore % "test")

lazy val utilRegistry = Project(
Expand All @@ -472,7 +507,7 @@ lazy val utilRegistry = Project(
name := "util-registry",
libraryDependencies ++= Seq(
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"
("org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilCore)

Expand All @@ -494,10 +529,10 @@ lazy val utilSlf4jApi = Project(
name := "util-slf4j-api",
libraryDependencies ++= Seq(
slf4jApi,
"org.mockito" %% "mockito-scala" % mockitoScalaVersion % "test",
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.slf4j" % "slf4j-simple" % slf4jVersion % "test"
)
).dependsOn(utilCore % "test", utilMock % "test")
).dependsOn(utilCore % "test")

lazy val utilSlf4jJulBridge = Project(
id = "util-slf4j-jul-bridge",
Expand All @@ -519,7 +554,7 @@ lazy val utilSecurity = Project(
libraryDependencies ++= Seq(
scalacheckLib,
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test"
)
).map(_.cross(CrossVersion.for3Use2_13))
).dependsOn(utilCore, utilLogging, utilSecurityTestCerts % "test")

lazy val utilSecurityTestCerts = Project(
Expand All @@ -541,15 +576,18 @@ lazy val utilStats = Project(
libraryDependencies ++= Seq(
caffeineLib,
jsr305Lib,
scalacheckLib,
"com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion,
"com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion exclude ("com.google.guava", "guava"),
"org.mockito" % "mockito-core" % mockitoVersion % "test",
) ++ Seq(
scalacheckLib,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion exclude ("com.google.guava", "guava"),
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test",
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test"
) ++ {
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test",
).map(_.cross(CrossVersion.for3Use2_13)) ++ {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, major)) =>
Seq("org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.3" % "test")
case Some((2, major)) if major >= 13 =>
Seq("org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0" % "test")
case _ =>
Expand All @@ -567,9 +605,12 @@ lazy val utilTest = Project(
name := "util-test",
libraryDependencies ++= Seq(
"org.mockito" % "mockito-all" % "1.10.19",
"org.scalatest" %% "scalatest" % "3.1.2",
"org.scalatestplus" %% "junit-4-12" % "3.1.2.0",
"org.scalatestplus" %% "mockito-1-10" % "3.1.0.0"
(if (isScala3(scalaBinaryVersion.value))
"org.scalatest" %% "scalatest" % "3.2.9"
else
"org.scalatest" %% "scalatest" % "3.1.2"),
("org.scalatestplus" %% "junit-4-12" % "3.1.2.0").cross(CrossVersion.for3Use2_13),
("org.scalatestplus" %% "mockito-1-10" % "3.1.0.0").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilCore, utilLogging)

Expand All @@ -595,10 +636,13 @@ lazy val utilTunable = Project(
sharedSettings
).settings(
name := "util-tunable",
resolvers += // TODO Scala3 remove this once the library is published properly
"Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
libraryDependencies ++= Seq(
"com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion,
"com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion exclude ("com.google.guava", "guava")
//("com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion exclude ("com.google.guava", "guava")).cross(CrossVersion.for3Use2_13)
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.13.0-SNAPSHOT", // TODO Scala3 global jackson upgrade first?
)
).dependsOn(utilApp, utilCore)

Expand All @@ -611,15 +655,16 @@ lazy val utilValidator = Project(
name := "util-validator",
libraryDependencies ++= Seq(
caffeineLib,
scalacheckLib,
"jakarta.validation" % "jakarta.validation-api" % "3.0.0",
"org.hibernate.validator" % "hibernate-validator" % "7.0.1.Final",
"org.glassfish" % "jakarta.el" % "4.0.0",
"org.json4s" %% "json4s-core" % "3.6.7",
"com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion % "test",
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test",
"org.slf4j" % "slf4j-simple" % slf4jVersion % "test"
)
) ++ Seq(
"org.json4s" %% "json4s-core" % "3.6.7",
scalacheckLib,
"org.scalatestplus" %% "scalacheck-1-14" % "3.1.2.0" % "test",
).map(_.cross(CrossVersion.for3Use2_13))
).dependsOn(utilCore, utilReflect, utilSlf4jApi)

lazy val utilZk = Project(
Expand All @@ -632,7 +677,7 @@ lazy val utilZk = Project(
libraryDependencies ++= Seq(
zkDependency,
"org.mockito" % "mockito-core" % mockitoVersion % "test",
"org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test"
("org.scalatestplus" %% "mockito-3-3" % "3.1.2.0" % "test").cross(CrossVersion.for3Use2_13)
)
).dependsOn(utilCore, utilLogging)

Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ resolvers += Classpaths.sbtPluginReleases
resolvers += Resolver.sonatypeRepo("snapshots")

addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.8.2")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.0")
2 changes: 1 addition & 1 deletion util-app/src/main/scala/com/twitter/app/ClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private[app] sealed abstract class ClassPath[CpInfo <: ClassPath.Info] {
// TODO - add suppport for the ModulePath after dropping JDK 8 support.
private[this] def urlsFromClasspath(): Array[URL] = {
val classpath: String = System.getProperty("java.class.path")
classpath.split(File.pathSeparator).map { pathEntry: String =>
classpath.split(File.pathSeparator).map { (pathEntry: String) =>
Paths.get(pathEntry).toAbsolutePath().toUri().toURL
}
}
Expand Down
8 changes: 4 additions & 4 deletions util-app/src/main/scala/com/twitter/app/Flaggable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,19 @@ object Flaggable {

implicit val ofInt: Flaggable[Int] = mandatory(_.toInt)
implicit val ofJavaInteger: Flaggable[JInteger] =
mandatory { s: String => JInteger.valueOf(s.toInt) }
mandatory { (s: String) => JInteger.valueOf(s.toInt) }

implicit val ofLong: Flaggable[Long] = mandatory(_.toLong)
implicit val ofJavaLong: Flaggable[JLong] =
mandatory { s: String => JLong.valueOf(s.toLong) }
mandatory { (s: String) => JLong.valueOf(s.toLong) }

implicit val ofFloat: Flaggable[Float] = mandatory(_.toFloat)
implicit val ofJavaFloat: Flaggable[JFloat] =
mandatory { s: String => JFloat.valueOf(s.toFloat) }
mandatory { (s: String) => JFloat.valueOf(s.toFloat) }

implicit val ofDouble: Flaggable[Double] = mandatory(_.toDouble)
implicit val ofJavaDouble: Flaggable[JDouble] =
mandatory { s: String => JDouble.valueOf(s.toDouble) }
mandatory { (s: String) => JDouble.valueOf(s.toDouble) }

// Conversions for common non-primitive types and collections.
implicit val ofDuration: Flaggable[Duration] = mandatory(Duration.parse(_))
Expand Down
6 changes: 4 additions & 2 deletions util-app/src/main/scala/com/twitter/app/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.collection.immutable.TreeSet
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.jdk.CollectionConverters._
import scala.reflect.ClassTag
import scala.util.control.NonFatal

/**
Expand Down Expand Up @@ -277,8 +278,9 @@ class Flags(argv0: String, includeGlobal: Boolean, failFastUntilParsed: Boolean)
* @param name The name of the flag.
* @param help The help string of the flag.
*/
def apply[T](name: String, help: String)(implicit _f: Flaggable[T], m: Manifest[T]): Flag[T] = {
val f = new Flag[T](name, help, m.toString, failFastUntilParsed)
def apply[T](name: String, help: String)(implicit _f: Flaggable[T], m: ClassTag[T]): Flag[T] = {
// TODO Scala3 Is ClassTag ok? Should we capitalize the type name?
val f = new Flag[T](name, help, m.runtimeClass.getTypeName.capitalize, failFastUntilParsed)
add(f)
f
}
Expand Down
6 changes: 4 additions & 2 deletions util-app/src/main/scala/com/twitter/app/GlobalFlag.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.twitter.app

import java.lang.reflect.Modifier
import scala.reflect.ClassTag
import scala.util.control.NonFatal

/**
Expand Down Expand Up @@ -85,8 +86,9 @@ abstract class GlobalFlag[T] private[app] (
*
* @param help documentation regarding usage of this [[Flag]].
*/
def this(help: String)(implicit _f: Flaggable[T], m: Manifest[T]) =
this(Right(m.toString), help)
def this(help: String)(implicit _f: Flaggable[T], m: ClassTag[T]) =
// TODO Scala3 Is ClassTag ok? Should we capitalize the type name?
this(Right(m.runtimeClass.getTypeName.capitalize), help)

/**
* The "name", or "id", of this [[Flag]].
Expand Down
2 changes: 1 addition & 1 deletion util-app/src/test/scala/com/twitter/app/AppTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class AppTest extends AnyFunSuite {
assert(n2 == 0)
assert(!f.isDefined)

p.setDone
p.setDone()
assert(n2 == 1)
assert(f.isDefined)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object GuavaCache {
*/
def fromLoadingCache[K, V](cache: LoadingCache[K, Future[V]]): K => Future[V] = {
val evicting = EvictingCache.lazily(new LoadingFutureCache(cache));
{ key: K => evicting.get(key).get.interruptible() }
{ (key: K) => evicting.get(key).get.interruptible() }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ConcurrentMapCache[K, V](underlying: ConcurrentMap[K, Future[V]]) extends
def set(key: K, value: Future[V]): Unit = underlying.put(key, value)

def getOrElseUpdate(key: K)(compute: => Future[V]): Future[V] = {
val p = Promise[V]
val p = Promise[V]()
underlying.putIfAbsent(key, p) match {
case null =>
p.become(compute)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ object FutureCache {
* @see [[standard]] for the equivalent Java API.
*/
def default[K, V](fn: K => Future[V], cache: FutureCache[K, V]): K => Future[V] =
AsyncMemoize(fn, new EvictingCache(cache)) andThen { f: Future[V] => f.interruptible() }
AsyncMemoize(fn, new EvictingCache(cache)) andThen { (f: Future[V]) => f.interruptible() }

/**
* Alias for [[default]] which can be called from Java.
Expand Down
Loading