Skip to content

Commit 92e103e

Browse files
authored
add Scala 2.13 support (#320)
* add Scala 2.13 cross build * fix HeavyHitters import * fix breakage * add 2.13 CircleCI build * remove parallel collection usage * scalafmt * improve CanBuild * bump version to 0.5.0-SNAPSHOT
1 parent 55c0230 commit 92e103e

21 files changed

Lines changed: 187 additions & 171 deletions

File tree

.circleci/config.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ scala_212: &scala_212
88
environment:
99
- SCALA_VERSION: 2.12.10
1010

11+
scala_213: &scala_213
12+
environment:
13+
- SCALA_VERSION: 2.13.1
14+
1115
jdk8: &jdk8
1216
docker:
1317
- image: circleci/openjdk:8-jdk
@@ -69,6 +73,12 @@ jobs:
6973
- build_featran:
7074
build-steps:
7175
- run: sbt "++$SCALA_VERSION test"
76+
build_213:
77+
<<: [*settings, *scala_213, *jdk8]
78+
steps:
79+
- build_featran:
80+
build-steps:
81+
- run: sbt "++$SCALA_VERSION test"
7282

7383
build_212_jdk11:
7484
<<: [*settings, *scala_212, *jdk11]
@@ -101,11 +111,16 @@ workflows:
101111
- build_212_jdk11:
102112
requires:
103113
- checks
114+
- build_213:
115+
requires:
116+
- checks
117+
- mima_report
104118

105119
- publish:
106120
requires:
107121
- build_211
108122
- build_212
123+
- build_213
109124
filters:
110125
branches:
111126
only:

build.sbt

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import sbtrelease.ReleaseStateTransformations._
2121

2222
val algebirdVersion = "0.13.6"
2323
val breezeVersion = "1.0"
24-
val circeVersion = "0.11.2"
24+
val circeVersion = "0.12.3"
2525
val commonsMathVersion = "3.6.1"
2626
val flinkVersion = "1.9.1"
2727
val hadoopVersion = "3.2.1"
@@ -44,14 +44,29 @@ lazy val commonSettings = Seq(
4444
description := "Feature Transformers",
4545
scalaVersion := "2.12.10",
4646
scalacOptions ++= commonScalacOptions,
47+
scalacOptions ++= {
48+
if (scalaBinaryVersion.value == "2.13")
49+
Seq("-Ymacro-annotations")
50+
else
51+
Seq(
52+
"-Xfuture",
53+
"-Yno-adapted-args"
54+
)
55+
},
4756
scalacOptions in (Compile, doc) ++= Seq("-skip-packages", "org.apache"),
4857
javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-Xlint:unchecked"),
4958
javacOptions in (Compile, doc) := Seq("-source", "1.8"),
5059
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "3"),
5160
libraryDependencies ++= Seq(
52-
"org.typelevel" %% "simulacrum" % simulacrumVersion % CompileTime,
53-
compilerPlugin("org.scalamacros" %% "paradise" % "2.1.1" cross CrossVersion.full)
61+
"org.typelevel" %% "simulacrum" % simulacrumVersion % CompileTime
5462
),
63+
libraryDependencies ++= {
64+
if (scalaBinaryVersion.value == "2.13") {
65+
Nil
66+
} else {
67+
Seq(compilerPlugin("org.scalamacros" %% "paradise" % "2.1.1" cross CrossVersion.full))
68+
}
69+
},
5570
ivyConfigurations += CompileTime,
5671
unmanagedClasspath in Compile ++= update.value.select(configurationFilter(CompileTime.name))
5772
)
@@ -61,23 +76,11 @@ lazy val publishSettings = Seq(
6176
username <- sys.env.get("SONATYPE_USERNAME")
6277
password <- sys.env.get("SONATYPE_PASSWORD")
6378
} yield Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", username, password)).toSeq,
64-
publishTo := sonatypePublishToBundle.value,
79+
publishTo := Some(
80+
if (isSnapshot.value) Opts.resolver.sonatypeSnapshots else Opts.resolver.sonatypeStaging
81+
),
6582
releaseCrossBuild := true,
6683
releasePublishArtifactsAction := PgpKeys.publishSigned.value,
67-
releaseProcess := Seq[ReleaseStep](
68-
checkSnapshotDependencies,
69-
inquireVersions,
70-
runClean,
71-
releaseStepCommandAndRemaining("+test"),
72-
setReleaseVersion,
73-
commitReleaseVersion,
74-
tagRelease,
75-
releaseStepCommandAndRemaining("+publishSigned"),
76-
releaseStepCommand("sonatypeBundleRelease"),
77-
setNextVersion,
78-
commitNextVersion,
79-
pushChanges
80-
),
8184
publishMavenStyle := true,
8285
publishArtifact in Test := false,
8386
sonatypeProfileName := "com.spotify",
@@ -177,7 +180,7 @@ lazy val core: Project = project
177180
name := "core",
178181
moduleName := "featran-core",
179182
description := "Feature Transformers",
180-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
183+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
181184
libraryDependencies ++= Seq(
182185
"com.twitter" %% "algebird-core" % algebirdVersion,
183186
"org.scalanlp" %% "breeze" % breezeVersion,
@@ -189,7 +192,7 @@ lazy val core: Project = project
189192
"io.circe" %% "circe-core",
190193
"io.circe" %% "circe-generic",
191194
"io.circe" %% "circe-parser"
192-
).map(_ % circeVersion)
195+
).map(_ % (if (scalaBinaryVersion.value == "2.11") "0.11.2" else circeVersion))
193196
)
194197

195198
lazy val java: Project = project
@@ -200,7 +203,7 @@ lazy val java: Project = project
200203
name := "java",
201204
moduleName := "featran-java",
202205
description := "Feature Transformers - java",
203-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
206+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
204207
libraryDependencies ++= Seq(
205208
"org.scalacheck" %% "scalacheck" % scalacheckVersion % "test",
206209
"org.scalatest" %% "scalatest" % scalatestVersion % "test"
@@ -300,7 +303,7 @@ lazy val numpy: Project = project
300303
name := "numpy",
301304
moduleName := "featran-numpy",
302305
description := "Feature Transformers - NumPy",
303-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
306+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
304307
libraryDependencies ++= Seq(
305308
"org.scalatest" %% "scalatest" % scalatestVersion % "test"
306309
)
@@ -315,7 +318,7 @@ lazy val tensorflow: Project = project
315318
name := "tensorflow",
316319
moduleName := "featran-tensorflow",
317320
description := "Feature Transformers - TensorFlow",
318-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
321+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
319322
libraryDependencies ++= Seq(
320323
"org.tensorflow" % "proto" % tensorflowVersion,
321324
"me.lyh" %% "shapeless-datatype-tensorflow" % shapelessDatatypeVersion,
@@ -335,7 +338,7 @@ lazy val xgboost: Project = project
335338
name := "xgboost",
336339
moduleName := "featran-xgboost",
337340
description := "Feature Transformers - XGBoost",
338-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
341+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
339342
libraryDependencies ++= Seq(
340343
"org.scalacheck" %% "scalacheck" % scalacheckVersion % "test"
341344
)
@@ -350,7 +353,7 @@ lazy val examples: Project = project
350353
.settings(featranSettings)
351354
.settings(soccoSettings)
352355
.settings(
353-
crossScalaVersions := Seq("2.11.12", "2.12.10"),
356+
crossScalaVersions := Seq("2.11.12", "2.12.10", "2.13.1"),
354357
name := "examples",
355358
moduleName := "featran-examples",
356359
description := "Feature Transformers - examples",
@@ -388,10 +391,8 @@ lazy val commonScalacOptions = Seq(
388391
"-language:higherKinds",
389392
"-language:implicitConversions",
390393
"-unchecked",
391-
"-Yno-adapted-args",
392394
"-Ywarn-unused",
393-
"-Ywarn-dead-code",
394-
"-Xfuture"
395+
"-Ywarn-dead-code"
395396
)
396397

397398
lazy val soccoSettings = if (sys.env.contains("SOCCO")) {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2017 Spotify AB.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13+
* KIND, either express or implied. See the License for the
14+
* specific language governing permissions and limitations
15+
* under the License.
16+
*/
17+
18+
package com.spotify.featran
19+
20+
import scala.collection.mutable
21+
import scala.reflect.ClassTag
22+
23+
// Workaround for CanBuildFrom not serializable
24+
trait CanBuild[T, M[_]] extends Serializable {
25+
def apply(): mutable.Builder[T, M[T]]
26+
}
27+
28+
object CanBuild {
29+
// Collection types in _root_.scala.*
30+
implicit def iterableCB[T]: CanBuild[T, Iterable] = new CanBuild[T, Iterable] {
31+
override def apply(): mutable.Builder[T, Iterable[T]] = Iterable.newBuilder
32+
}
33+
34+
implicit def seqCB[T]: CanBuild[T, Seq] = new CanBuild[T, Seq] {
35+
override def apply(): mutable.Builder[T, Seq[T]] = Seq.newBuilder
36+
}
37+
38+
implicit def indexedSeqCB[T]: CanBuild[T, IndexedSeq] = new CanBuild[T, IndexedSeq] {
39+
override def apply(): mutable.Builder[T, IndexedSeq[T]] = IndexedSeq.newBuilder
40+
}
41+
42+
implicit def listCB[T]: CanBuild[T, List] = new CanBuild[T, List] {
43+
override def apply(): mutable.Builder[T, List[T]] = List.newBuilder
44+
}
45+
46+
implicit def vectorCB[T]: CanBuild[T, Vector] = new CanBuild[T, Vector] {
47+
override def apply(): mutable.Builder[T, Vector[T]] = Vector.newBuilder
48+
}
49+
50+
implicit def bufferCB[T]: CanBuild[T, mutable.Buffer] = new CanBuild[T, mutable.Buffer] {
51+
override def apply(): mutable.Builder[T, mutable.Buffer[T]] = mutable.Buffer.newBuilder
52+
}
53+
54+
implicit def floatArrayCB: CanBuild[Float, Array] = new CanBuild[Float, Array] {
55+
override def apply(): mutable.Builder[Float, Array[Float]] = Array.newBuilder[Float]
56+
}
57+
58+
implicit def doubleArrayCB: CanBuild[Double, Array] = new CanBuild[Double, Array] {
59+
override def apply(): mutable.Builder[Double, Array[Double]] = Array.newBuilder[Double]
60+
}
61+
62+
implicit def arrayCB[T: ClassTag]: CanBuild[T, Array] = new CanBuild[T, Array] {
63+
override def apply(): mutable.Builder[T, Array[T]] = Array.newBuilder[T]
64+
}
65+
}

core/src/main/scala/com/spotify/featran/CollectionType.scala

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package com.spotify.featran
1919

2020
import simulacrum._
2121

22-
import scala.collection.generic.CanBuildFrom
2322
import scala.collection.mutable
2423
import scala.reflect.ClassTag
2524

@@ -38,36 +37,36 @@ import scala.reflect.ClassTag
3837
}
3938

4039
object CollectionType {
41-
implicit def scalaCollectionType[M[_] <: Traversable[_]](
42-
implicit cbf: CanBuildFrom[M[_], _, M[_]]
40+
implicit def scalaCollectionType[M[_]](
41+
implicit cb: CanBuild[_, M],
42+
ti: M[_] => Iterable[_]
4343
): CollectionType[M] =
4444
new CollectionType[M] {
4545
override def map[A, B: ClassTag](ma: M[A])(f: A => B): M[B] = {
46-
val builder = cbf().asInstanceOf[mutable.Builder[B, M[B]]]
47-
ma.asInstanceOf[Seq[A]].foreach(a => builder += f(a))
46+
val builder = cb().asInstanceOf[mutable.Builder[B, M[B]]]
47+
ma.asInstanceOf[Iterable[A]].foreach(a => builder += f(a))
4848
builder.result()
4949
}
5050

5151
override def pure[A, B: ClassTag](ma: M[A])(b: B): M[B] = {
52-
val builder = cbf().asInstanceOf[mutable.Builder[B, M[B]]]
52+
val builder = cb().asInstanceOf[mutable.Builder[B, M[B]]]
5353
builder += b
5454
builder.result()
5555
}
5656

5757
override def reduce[A](ma: M[A])(f: (A, A) => A): M[A] = {
58-
val builder = cbf().asInstanceOf[mutable.Builder[A, M[A]]]
59-
if (ma.asInstanceOf[Seq[A]].nonEmpty) {
60-
builder += ma.asInstanceOf[Seq[A]].reduce(f)
58+
val builder = cb().asInstanceOf[mutable.Builder[A, M[A]]]
59+
if (ma.nonEmpty) {
60+
builder += ma.asInstanceOf[Iterable[A]].reduce(f)
6161
}
6262
builder.result()
6363
}
6464

6565
override def cross[A, B: ClassTag](ma: M[A])(mb: M[B]): M[(A, B)] = {
66-
val builder = cbf().asInstanceOf[mutable.Builder[(A, B), M[(A, B)]]]
67-
val seq = mb.asInstanceOf[Seq[B]]
68-
if (seq.nonEmpty) {
69-
val b = mb.asInstanceOf[Seq[B]].head
70-
ma.asInstanceOf[Seq[A]].foreach(a => builder += ((a, b)))
66+
val builder = cb().asInstanceOf[mutable.Builder[(A, B), M[(A, B)]]]
67+
if (mb.nonEmpty) {
68+
val b = mb.asInstanceOf[Iterable[B]].head
69+
ma.asInstanceOf[Iterable[A]].foreach(a => builder += ((a, b)))
7170
}
7271
builder.result()
7372
}

core/src/main/scala/com/spotify/featran/CrossingFeatureBuilder.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ private case class Crossings(map: Crossings.MAP, keys: Set[String]) {
4646

4747
def filter[T](predicate: String => Boolean): Crossings = {
4848
val filteredKeys = keys.filter(predicate)
49-
val filteredMap = map.filterKeys {
49+
val b = SortedMap.newBuilder[Crossings.KEY, Crossings.FN]
50+
b ++= map.filterKeys {
5051
case (k1, k2) => filteredKeys.contains(k1) || filteredKeys.contains(k2)
5152
}
53+
val filteredMap = b.result()
5254

5355
Crossings(filteredMap, filteredKeys)
5456
}

0 commit comments

Comments
 (0)