Skip to content

Commit af0c28e

Browse files
authored
Merge pull request #115 from smarter/preserve-bin-compat
Preserve binary compatibility for Factory
2 parents 80548cc + 72ac06b commit af0c28e

File tree

8 files changed

+115
-39
lines changed

8 files changed

+115
-39
lines changed

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ matrix:
5454
scala: 2.12.6
5555
env: TEST_SCALAFIX=true
5656

57+
# run binary compatibility test
58+
- jdk: oraclejdk8
59+
scala: 2.12.6
60+
env: TEST_BINARY_COMPAT=true
61+
5762
# | jdk | scala | scala target | scala target version | scalafix test |
5863
# | ----------- | --------- | ------------ | -------------------- |---------------|
5964
# | openjdk6 | 2.11.12 | jvm | | |

admin/build.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ if [ "$SCALAJS_VERSION" = "" ]; then
2222
if [[ "$TEST_SCALAFIX" == "true" ]]; then
2323
projectPrefix="scalafixRules"
2424
else
25-
projectPrefix="compat"
25+
if [[ "$TEST_BINARY_COMPAT" == "true" ]]; then
26+
projectPrefix="binary-compat"
27+
else
28+
projectPrefix="compat"
29+
fi
2630
fi
2731
else
2832
projectPrefix="compatJS"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.example
2+
3+
import scala.collection.generic._
4+
import scala.collection.compat._
5+
6+
object Lib {
7+
def id[A, C[X] <: Iterable[X]](x: C[A])(implicit factory: Factory[A, C[A]]) = {
8+
val builder = factory.newBuilder
9+
builder ++= x
10+
builder.result
11+
}
12+
13+
def test1 =
14+
id(List(1, 2, 3))
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.example
2+
3+
import scala.collection.generic._
4+
5+
object Lib {
6+
def id[A, C[X] <: Iterable[X]](x: C[A])(implicit cbf: CanBuildFrom[C[A], A, C[A]]) = {
7+
val builder = cbf()
8+
builder ++= x
9+
builder.result
10+
}
11+
12+
def test1 =
13+
id(List(1, 2, 3))
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import org.junit.{Assert, Test}
2+
3+
import build.BuildInfo._
4+
5+
import com.typesafe.tools.mima.lib.MiMaLib
6+
import com.typesafe.tools.mima.core.Config
7+
8+
class BinaryCompaTest {
9+
@Test
10+
def compat(): Unit = {
11+
Config.setup("foo", Array(oldClasspath, newClasspath))
12+
val mima = new MiMaLib(Config.baseClassPath)
13+
val allProblems = mima.collectProblems(oldClasspath, newClasspath)
14+
Assert.assertEquals(allProblems, Nil)
15+
}
16+
}

build.sbt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ lazy val root = project
1414

1515
// == Core Libraries ==
1616

17+
lazy val junit = libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test
18+
1719
lazy val compat = crossProject(JSPlatform, JVMPlatform)
1820
.withoutSuffixFor(JVMPlatform)
1921
.crossType(CrossType.Pure)
@@ -32,7 +34,7 @@ lazy val compat = crossProject(JSPlatform, JVMPlatform)
3234
)
3335
.jvmSettings(
3436
OsgiKeys.exportPackage := Seq(s"scala.collection.compat.*;version=${version.value}"),
35-
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
37+
junit,
3638
javaHome in Compile := {
3739
val oldValue = (javaHome in Compile).value
3840
val isOnCi = sys.env.get("CI").isDefined
@@ -65,6 +67,36 @@ lazy val compat = crossProject(JSPlatform, JVMPlatform)
6567
lazy val compatJVM = compat.jvm
6668
lazy val compatJS = compat.js
6769

70+
lazy val `binary-compat-old` = project
71+
.in(file("binary-compat/old"))
72+
.settings(scalaVersion := scala212)
73+
.disablePlugins(ScalafixPlugin)
74+
75+
lazy val `binary-compat-new` = project
76+
.in(file("binary-compat/new"))
77+
.settings(scalaVersion := scala212)
78+
.dependsOn(compatJVM)
79+
.disablePlugins(ScalafixPlugin)
80+
81+
lazy val `binary-compat` = project
82+
.in(file("binary-compat/test"))
83+
.settings(
84+
scalaVersion := scala212,
85+
libraryDependencies += "com.typesafe" %% "mima-reporter" % "0.3.0" % Test,
86+
junit,
87+
buildInfoPackage := "build",
88+
buildInfoKeys := Seq[BuildInfoKey](
89+
"oldClasspath" -> (classDirectory in (`binary-compat-old`, Compile)).value.toString,
90+
"newClasspath" -> (classDirectory in (`binary-compat-new`, Compile)).value.toString
91+
),
92+
test in Test := (test in Test).dependsOn(
93+
compile in (`binary-compat-old`, Compile),
94+
compile in (`binary-compat-new`, Compile)
95+
).value
96+
)
97+
.enablePlugins(BuildInfoPlugin)
98+
.disablePlugins(ScalafixPlugin)
99+
68100
lazy val scalafixRules = project
69101
.in(file("scalafix/rules"))
70102
.settings(

compat/src/main/scala-2.11_2.12/scala/collection/compat/Factory.scala

Lines changed: 0 additions & 37 deletions
This file was deleted.

compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,33 @@ import scala.{collection => c}
1010
private[compat] trait PackageShared {
1111
import CompatImpl._
1212

13+
/**
14+
* A factory that builds a collection of type `C` with elements of type `A`.
15+
*
16+
* @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.)
17+
* @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.)
18+
*/
19+
type Factory[-A, +C] <: CanBuildFrom[Nothing, A, C] // Ideally, this would be an opaque type
20+
21+
implicit class FactoryOps[-A, +C](private val factory: Factory[A, C]) {
22+
/**
23+
* @return A collection of type `C` containing the same elements
24+
* as the source collection `it`.
25+
* @param it Source collection
26+
*/
27+
def fromSpecific(it: TraversableOnce[A]): C = (factory() ++= it).result()
28+
29+
/** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer.
30+
* Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */
31+
def newBuilder: m.Builder[A, C] = factory()
32+
}
33+
34+
implicit def fromCanBuildFrom[A, C](implicit cbf: CanBuildFrom[Nothing, A, C]): Factory[A, C] =
35+
cbf.asInstanceOf[Factory[A, C]]
36+
37+
implicit def fromCanBuildFromConversion[X, A, C](x: X)(implicit toCanBuildFrom: X => CanBuildFrom[Nothing, A, C]): Factory[A, C] =
38+
fromCanBuildFrom(toCanBuildFrom(x))
39+
1340
implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]](
1441
fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] =
1542
simpleCBF(fact.newBuilder[A])

0 commit comments

Comments
 (0)