sbt / sbt-osgi

sbt plugin for creating OSGi bundles
Apache License 2.0
47 stars 43 forks source link

Translate Scala-style version numbers for OSGi #15

Open szeiger opened 10 years ago

szeiger commented 10 years ago

The current direct translation from Scala-style version numbers (as used for publishing in Maven repositories) to OSGi version numbers makes it difficult to get import version ranges right.

For instance, this is what we now do in slick-extensions:

  OsgiKeys.importPackage := Seq(
    osgiImport("scala.slick*", slickVersion, requireMicro=true),
    osgiImport("scala*", scalaVersion.value),
    "*"
  )

Where osgiImport is defined as:

  /** Create an OSGi version range for standard Scala / Typesafe versioning
    * schemes that describes binary compatible versions. */
  def osgiVersionRange(version: String, requireMicro: Boolean = false): String =
    if(version contains '-') "${@}" // M, RC or SNAPSHOT -> exact version
    else if(requireMicro) "${range;[===,=+)}" // At least the same micro version
    else "${range;[==,=+)}" // Any binary compatible version

  /** Create an OSGi Import-Package version specification. */
  def osgiImport(pattern: String, version: String, requireMicro: Boolean = false): String =
    pattern + ";version=\"" + osgiVersionRange(version, requireMicro) + "\""

This means that slick-extensions 2.0.2 imports slick [2.0.2,2.1) and slick-extensions 2.1.0 imports slick [2.1.0,2.2), which are the defined binary compatible versions. When we look at non-release versions (which have no binary compatibility guarantees), this also works fine: slick-extensions 2.1.0.M1 imports slick 2.1.0.M1.

Combining either multiple release versions, or multiple non-release versions in a container also poses no problems, but things break down when you try to mix release and non-release versions like slick-extensions 2.1.0.M1 and 2.1.0 because 2.1.0.M1 is matched by [2.1.0,2.2) even though it is incompatible and older than 2.1.0.

I think sbt-osgi would be a good place to solve this for all Scala libraries. It could translate the version from the sbt build to an OSGi version for the bundle and the exported packages, and do the same to dependency versions (or is that fully handled by bnd?).

I have some ideas how a suitable version number scheme could look but I'm sure this problem has already been solved by the OSGi community, so I'll leave it to others to suggest something.

As far as I can tell, the challenges are:

reggert commented 10 years ago

Unfortunately, OSGi and Maven have very different views as to how version qualifiers should be handled. Maven primarily uses qualifiers for pre-release builds, whereas OSGi treats versions with qualifiers as being newer than versions without qualifiers. IIRC, there was an attempt to add some sort of pre-release qualifier support to the OSGi spec, but to the best of my knowledge, that attempt was aborted.

reggert commented 10 years ago

Also note that it is not guaranteed that the package-level version numbers will match the bundle-level or POM-level version numbers for a given dependency, though sbt-osgi (and maven-bundle-plugin) will make them match by default when creating bundles.

E.g., it may be possible that the foo:bar:1.0.0:jar artifact will contain packages a.b.c;version="1.0.1" and d.e.f;version="2.0.0".

reggert commented 10 years ago

I'm not sure that it's possible to solve this generically in a way that will work for all projects, since different projects are likely to have different policies regarding dependency versions. That said, it may feasible to provide a way to at least generate the appropriate package import for the Scala standard library in a uniform way, since that's likely to be the same across all Scala projects.