Skip to content

Commit fb083e3

Browse files
committed
Add support for multi-release dependencies (sbt#1243)
1 parent 6e9fab4 commit fb083e3

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

src/main/scala/com/typesafe/sbt/packager/archetypes/jlink/JlinkPlugin.scala

+31-7
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,31 @@ object JlinkPlugin extends AutoPlugin {
4949
jlinkModules := (jlinkModules ?? Nil).value,
5050
jlinkModules ++= {
5151
val log = streams.value.log
52-
val run = runJavaTool(javaHome.in(jlinkBuildImage).value, log) _
52+
val javaHome0 = javaHome.in(jlinkBuildImage).value.getOrElse(defaultJavaHome)
53+
val run = runJavaTool(javaHome0, log) _
5354
val paths = fullClasspath.in(jlinkBuildImage).value.map(_.data.getPath)
5455
val shouldIgnore = jlinkIgnoreMissingDependency.value
5556

57+
// We can find the java toolchain version by parsing the `release` file. This
58+
// only works for Java 9+, but so does this whole plugin.
59+
// Alternatives:
60+
// - Parsing `java -version` output - the format is not standardized, so there
61+
// are a lot of weird incompatibilities.
62+
// - Parsing `java -XshowSettings:properties` output - the format is nicer,
63+
// but the command itself is subject to change without notice.
64+
val releaseFile = javaHome0 / "release"
65+
val javaVersion = IO
66+
.readLines(releaseFile)
67+
.collectFirst {
68+
case javaVersionPattern(feature) => feature
69+
}
70+
.getOrElse(sys.error("JAVA_VERSION not found in ${releaseFile.getAbsolutePath}"))
71+
5672
// Jdeps has a few convenient options (like --print-module-deps), but those
5773
// are not flexible enough - we need to parse the full output.
58-
val output = runForOutput(run("jdeps", "-R" +: paths), log)
74+
val jdepsOutput = runForOutput(run("jdeps", "--multi-release" +: javaVersion +: "-R" +: paths), log)
5975

60-
val deps = output.linesIterator
76+
val deps = jdepsOutput.linesIterator
6177
// There are headers in some of the lines - ignore those.
6278
.flatMap(PackageDependency.parse(_).iterator)
6379
.toSeq
@@ -109,7 +125,8 @@ object JlinkPlugin extends AutoPlugin {
109125
},
110126
jlinkBuildImage := {
111127
val log = streams.value.log
112-
val run = runJavaTool(javaHome.in(jlinkBuildImage).value, log) _
128+
val javaHome0 = javaHome.in(jlinkBuildImage).value.getOrElse(defaultJavaHome)
129+
val run = runJavaTool(javaHome0, log) _
113130
val outDir = target.in(jlinkBuildImage).value
114131

115132
IO.delete(outDir)
@@ -130,15 +147,22 @@ object JlinkPlugin extends AutoPlugin {
130147
mappings in Universal ++= mappings.in(jlinkBuildImage).value
131148
)
132149

150+
// Extracts java version from a release file line (`JAVA_VERSION` property):
151+
// - if the feature version is 1, yield the minor version number (e.g. 1.9.0 -> 9);
152+
// - otherwise yield the major version number (e.g. 11.0.3 -> 11).
153+
private[jlink] val javaVersionPattern = """JAVA_VERSION="(?:1\.)?(\d+).*?"""".r
154+
133155
// TODO: deduplicate with UniversalPlugin and DebianPlugin
134156
/** Finds all files in a directory. */
135157
private def findFiles(dir: File): Seq[(File, String)] =
136158
((PathFinder(dir) ** AllPassFilter) --- dir)
137159
.pair(file => IO.relativize(dir, file))
138160

139-
private def runJavaTool(jvm: Option[File], log: Logger)(exeName: String, args: Seq[String]): ProcessBuilder = {
140-
val jh = jvm.getOrElse(file(sys.props.getOrElse("java.home", sys.error("no java.home"))))
141-
val exe = (jh / "bin" / exeName).getAbsolutePath
161+
private lazy val defaultJavaHome: File =
162+
file(sys.props.getOrElse("java.home", sys.error("no java.home")))
163+
164+
private def runJavaTool(jvm: File, log: Logger)(exeName: String, args: Seq[String]): ProcessBuilder = {
165+
val exe = (jvm / "bin" / exeName).getAbsolutePath
142166

143167
log.info("Running: " + (exe +: args).mkString(" "))
144168

src/test/scala/com/typesafe/sbt/packager/archetypes/jlink/JlinkSpec.scala

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.typesafe.sbt.packager.archetypes.jlink
22

33
import org.scalatest.{FlatSpec, Matchers}
44
import JlinkPlugin.Ignore.byPackagePrefix
5+
import JlinkPlugin.javaVersionPattern
56

67
class JlinkSpec extends FlatSpec with Matchers {
78
"Ignore.byPackagePrefix()" should "match as expected for sample examples" in {
@@ -20,4 +21,11 @@ class JlinkSpec extends FlatSpec with Matchers {
2021
byPackagePrefix("foo" -> "bar", "" -> "")("baz" -> "qux") should be(true)
2122
byPackagePrefix("foo" -> "", "" -> "bar")("baz" -> "qux") should be(false)
2223
}
24+
25+
"javaVersionPattern" should "match known examples" in {
26+
"""JAVA_VERSION="11.0.3"""" should fullyMatch regex (javaVersionPattern withGroup "11")
27+
// Haven't seen this in the wild, but JEP220 has this example, so we might
28+
// as well handle it.
29+
"""JAVA_VERSION="1.9.0"""" should fullyMatch regex (javaVersionPattern withGroup "9")
30+
}
2331
}

0 commit comments

Comments
 (0)