Skip to content

Preserve binary compatibility for Factory #115

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

Merged
merged 4 commits into from
Jul 26, 2018
Merged
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
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ matrix:
scala: 2.12.6
env: TEST_SCALAFIX=true

# run binary compatibility test
- jdk: oraclejdk8
scala: 2.12.6
env: TEST_BINARY_COMPAT=true

# | jdk | scala | scala target | scala target version | scalafix test |
# | ----------- | --------- | ------------ | -------------------- |---------------|
# | openjdk6 | 2.11.12 | jvm | | |
Expand Down
6 changes: 5 additions & 1 deletion admin/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ if [ "$SCALAJS_VERSION" = "" ]; then
if [[ "$TEST_SCALAFIX" == "true" ]]; then
projectPrefix="scalafixRules"
else
projectPrefix="compat"
if [[ "$TEST_BINARY_COMPAT" == "true" ]]; then
projectPrefix="binary-compat"
else
projectPrefix="compat"
fi
fi
else
projectPrefix="compatJS"
Expand Down
15 changes: 15 additions & 0 deletions binary-compat/new/src/main/scala/org.example/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.example

import scala.collection.generic._
import scala.collection.compat._

object Lib {
def id[A, C[X] <: Iterable[X]](x: C[A])(implicit factory: Factory[A, C[A]]) = {
val builder = factory.newBuilder
builder ++= x
builder.result
}

def test1 =
id(List(1, 2, 3))
}
14 changes: 14 additions & 0 deletions binary-compat/old/src/main/scala/org.example/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.example

import scala.collection.generic._

object Lib {
def id[A, C[X] <: Iterable[X]](x: C[A])(implicit cbf: CanBuildFrom[C[A], A, C[A]]) = {
val builder = cbf()
builder ++= x
builder.result
}

def test1 =
id(List(1, 2, 3))
}
16 changes: 16 additions & 0 deletions binary-compat/test/src/test/scala/BinaryCompaTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import org.junit.{Assert, Test}

import build.BuildInfo._

import com.typesafe.tools.mima.lib.MiMaLib
import com.typesafe.tools.mima.core.Config

class BinaryCompaTest {
@Test
def compat(): Unit = {
Config.setup("foo", Array(oldClasspath, newClasspath))
val mima = new MiMaLib(Config.baseClassPath)
val allProblems = mima.collectProblems(oldClasspath, newClasspath)
Assert.assertEquals(allProblems, Nil)
}
}
34 changes: 33 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ lazy val root = project

// == Core Libraries ==

lazy val junit = libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test

lazy val compat = crossProject(JSPlatform, JVMPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
Expand All @@ -32,7 +34,7 @@ lazy val compat = crossProject(JSPlatform, JVMPlatform)
)
.jvmSettings(
OsgiKeys.exportPackage := Seq(s"scala.collection.compat.*;version=${version.value}"),
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
junit,
javaHome in Compile := {
val oldValue = (javaHome in Compile).value
val isOnCi = sys.env.get("CI").isDefined
Expand Down Expand Up @@ -65,6 +67,36 @@ lazy val compat = crossProject(JSPlatform, JVMPlatform)
lazy val compatJVM = compat.jvm
lazy val compatJS = compat.js

lazy val `binary-compat-old` = project
.in(file("binary-compat/old"))
.settings(scalaVersion := scala212)
.disablePlugins(ScalafixPlugin)

lazy val `binary-compat-new` = project
.in(file("binary-compat/new"))
.settings(scalaVersion := scala212)
.dependsOn(compatJVM)
.disablePlugins(ScalafixPlugin)

lazy val `binary-compat` = project
.in(file("binary-compat/test"))
.settings(
scalaVersion := scala212,
libraryDependencies += "com.typesafe" %% "mima-reporter" % "0.3.0" % Test,
junit,
buildInfoPackage := "build",
buildInfoKeys := Seq[BuildInfoKey](
"oldClasspath" -> (classDirectory in (`binary-compat-old`, Compile)).value.toString,
"newClasspath" -> (classDirectory in (`binary-compat-new`, Compile)).value.toString
),
test in Test := (test in Test).dependsOn(
compile in (`binary-compat-old`, Compile),
compile in (`binary-compat-new`, Compile)
).value
)
.enablePlugins(BuildInfoPlugin)
.disablePlugins(ScalafixPlugin)

lazy val scalafixRules = project
.in(file("scalafix/rules"))
.settings(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ import scala.{collection => c}
private[compat] trait PackageShared {
import CompatImpl._

/**
* A factory that builds a collection of type `C` with elements of type `A`.
*
* @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.)
* @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.)
*/
type Factory[-A, +C] <: CanBuildFrom[Nothing, A, C] // Ideally, this would be an opaque type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


implicit class FactoryOps[-A, +C](private val factory: Factory[A, C]) {
/**
* @return A collection of type `C` containing the same elements
* as the source collection `it`.
* @param it Source collection
*/
def fromSpecific(it: TraversableOnce[A]): C = (factory() ++= it).result()

/** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer.
* Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */
def newBuilder: m.Builder[A, C] = factory()
}

implicit def fromCanBuildFrom[A, C](implicit cbf: CanBuildFrom[Nothing, A, C]): Factory[A, C] =
cbf.asInstanceOf[Factory[A, C]]

implicit def fromCanBuildFromConversion[X, A, C](x: X)(implicit toCanBuildFrom: X => CanBuildFrom[Nothing, A, C]): Factory[A, C] =
fromCanBuildFrom(toCanBuildFrom(x))

implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]](
fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] =
simpleCBF(fact.newBuilder[A])
Expand Down