diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 6c3795bf09..fcf6c2fe77 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -11,12 +11,10 @@ import java.time.format.DateTimeFormatter import java.time.ZonedDateTime import java.time.ZoneOffset -// Overriding CrossJava imports #26935 -import sbt.Keys.{fullJavaHomes=>_,_} +import sbt.Keys._ import sbt._ -import CrossJava.autoImport._ -import org.scalafmt.sbt.ScalafmtPlugin.autoImport._ +import JdkOptions.autoImport._ import scala.collection.breakOut object AkkaBuild { @@ -132,14 +130,14 @@ object AkkaBuild { // compile options scalacOptions in Compile ++= DefaultScalacOptions, scalacOptions in Compile ++= - CrossJava.targetJdkScalacOptions(targetSystemJdk.value, fullJavaHomes.value), + JdkOptions.targetJdkScalacOptions(targetSystemJdk.value, fullJavaHomes.value), scalacOptions in Compile ++= (if (allWarnings) Seq("-deprecation") else Nil), scalacOptions in Test := (scalacOptions in Test).value.filterNot(opt => opt == "-Xlog-reflective-calls" || opt.contains("genjavadoc")), javacOptions in compile ++= DefaultJavacOptions ++ - CrossJava.targetJdkJavacOptions(targetSystemJdk.value, fullJavaHomes.value), + JdkOptions.targetJdkJavacOptions(targetSystemJdk.value, fullJavaHomes.value), javacOptions in test ++= DefaultJavacOptions ++ - CrossJava.targetJdkJavacOptions(targetSystemJdk.value, fullJavaHomes.value), + JdkOptions.targetJdkJavacOptions(targetSystemJdk.value, fullJavaHomes.value), javacOptions in compile ++= (if (allWarnings) Seq("-Xlint:deprecation") else Nil), javacOptions in doc ++= Seq(), @@ -250,14 +248,14 @@ object AkkaBuild { mavenLocalResolverSettings, docLintingSettings, - CrossJava.crossJavaSettings, + JdkOptions.targetJdkSettings, ) lazy val docLintingSettings = Seq( javacOptions in compile ++= Seq("-Xdoclint:none"), javacOptions in test ++= Seq("-Xdoclint:none"), javacOptions in doc ++= { - if (JavaVersion.isJdk8) Seq("-Xdoclint:none") + if (JdkOptions.isJdk8) Seq("-Xdoclint:none") else Seq("-Xdoclint:none", "--ignore-source-errors") } ) diff --git a/project/CrossJava.scala b/project/CrossJava.scala deleted file mode 100644 index 142258a71c..0000000000 --- a/project/CrossJava.scala +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2009-2019 Lightbend Inc. - */ - -package akka - -import java.io.File - -import scala.annotation.tailrec -import scala.collection.immutable.ListMap -import sbt._ -import sbt.librarymanagement.SemanticSelector -import sbt.librarymanagement.VersionNumber -import akka.CrossJava.nullBlank - -/* - * Tools for discovering different Java versions, - * - * Some of these will be in sbt 1.3.0 (https://github.com/sbt/sbt/pull/4139 et al) - * but are replicated here until that time - */ -case class JavaVersion(numbers: Vector[Long], vendor: Option[String]) { - def numberStr: String = numbers.mkString(".") - - def withVendor(vendor: Option[String]) = copy(vendor = vendor) - def withVendor(vendor: String) = copy(vendor = Option(vendor)) - def withNumbers(numbers: Vector[Long]) = copy(numbers = numbers) - - override def toString: String = { - vendor.map(_ + "@").getOrElse("") + numberStr - } -} -object JavaVersion { - - val specificationVersion: String = sys.props("java.specification.version") - - val version: String = sys.props("java.version") - - def isJdk8: Boolean = - VersionNumber(specificationVersion).matchesSemVer(SemanticSelector(s"=1.8")) - - val isJdk11orHigher: Boolean = - VersionNumber(specificationVersion).matchesSemVer(SemanticSelector(">=11")) - - def apply(version: String): JavaVersion = CrossJava.parseJavaVersion(version) - def apply(numbers: Vector[Long], vendor: String): JavaVersion = new JavaVersion(numbers, Option(vendor)) - - def notOnJdk8[T](values: Seq[T]): Seq[T] = if (isJdk8) Seq.empty[T] else values - - def sourceAndTarget(fullJavaHome: File): Seq[String] = - if (isJdk8) Seq.empty - else Seq("-source", "8", "-target", "8", "-bootclasspath", fullJavaHome + "/jre/lib/rt.jar") - -} - -object CrossJava extends AutoPlugin { - object autoImport { - val discoveredJavaHomes = settingKey[Map[String, File]]("Discovered Java home directories") - val javaHomes = settingKey[Map[String, File]]("The user-defined additional Java home directories") - val fullJavaHomes = settingKey[Map[String, File]]("Combines discoveredJavaHomes and custom javaHomes.") - val targetSystemJdk = settingKey[Boolean]("Target the system JDK instead of building against JDK 8. When this is enabled resulting artifacts may not work on JDK 8!") - } - import autoImport._ - - def targetJdkScalacOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File]): Seq[String] = - selectOptions( - targetSystemJdk, - fullJavaHomes, - Seq("-target:jvm-1.8"), - // '-release 8' is not enough, for some reason we need the 8 rt.jar - // explicitly. To test whether this has the desired effect, compile - // akka-remote and check the invocation of 'ByteBuffer.clear()' in - // EnvelopeBuffer.class with 'javap -c': it should refer to - //""java/nio/ByteBuffer.clear:()Ljava/nio/Buffer" and not - // "java/nio/ByteBuffer.clear:()Ljava/nio/ByteBuffer". Issue #27079 - (java8home: File) => Seq("-release", "8", "-javabootclasspath", java8home + "/jre/lib/rt.jar") - ) - def targetJdkJavacOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File]): Seq[String] = - selectOptions( - targetSystemJdk, - fullJavaHomes, - Nil, - // '-release 8' would be a neater option here, but is currently not an - // option because it doesn't provide access to `sun.misc.Unsafe` #27079 - (java8home: File) => Seq("-source", "8", "-target", "8", "-bootclasspath", java8home + "/jre/lib/rt.jar") - ) - - private def selectOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File], jdk8options: Seq[String], jdk11options: File => Seq[String]): Seq[String] = - if (targetSystemJdk) - Nil - else if (JavaVersion.isJdk8) - jdk8options - else fullJavaHomes.get("8") match { - case Some(java8home) => - jdk11options(java8home) - case None => - throw new MessageOnlyException("A JDK 8 installation was not found, but is required to build Akka. To target your system JDK, use the \"set every targetSystemJdk := true\" sbt command. Resulting artifacts may not work on JDK 8") - } - - val crossJavaSettings = Seq( - discoveredJavaHomes := CrossJava.discoverJavaHomes, - javaHomes := ListMap.empty, - fullJavaHomes := CrossJava.expandJavaHomes(discoveredJavaHomes.value ++ javaHomes.value), - targetSystemJdk := false, - ) - - // parses jabaa style version number adopt@1.8 - def parseJavaVersion(version: String): JavaVersion = { - def splitDot(s: String): Vector[Long] = - Option(s) match { - case Some(x) => x.split('.').toVector.filterNot(_ == "").map(_.toLong) - case _ => Vector() - } - def splitAt(s: String): Vector[String] = - Option(s) match { - case Some(x) => x.split('@').toVector - case _ => Vector() - } - splitAt(version) match { - case Vector(vendor, rest) => JavaVersion(splitDot(rest), Option(vendor)) - case Vector(rest) => JavaVersion(splitDot(rest), None) - case _ => sys.error(s"Invalid JavaVersion: $version") - } - } - - def discoverJavaHomes: ListMap[String, File] = { - ListMap(JavaDiscoverConfig.configs.flatMap { _.javaHomes }.sortWith(versionOrder): _*) - } - - sealed trait JavaDiscoverConf { - def javaHomes: Vector[(String, File)] - } - - def versionOrder(left: (_, File), right: (_, File)): Boolean = - versionOrder(left._2.getName, right._2.getName) - - // Sort version strings, considering 1.8.0 < 1.8.0_45 < 1.8.0_121 - @tailrec - def versionOrder(left: String, right: String): Boolean = { - val Pattern = """.*?([0-9]+)(.*)""".r - left match { - case Pattern(leftNumber, leftRest) => - right match { - case Pattern(rightNumber, rightRest) => - if (Integer.parseInt(leftNumber) < Integer.parseInt(rightNumber)) true - else if (Integer.parseInt(leftNumber) > Integer.parseInt(rightNumber)) false - else versionOrder(leftRest, rightRest) - case _ => - false - } - case _ => - true - } - } - - object JavaDiscoverConfig { - private val JavaHomeDir = """(java-|jdk-?|adoptopenjdk-)(1\.)?([0-9]+).*""".r - - class LinuxDiscoverConfig(base: File) extends JavaDiscoverConf { - def javaHomes: Vector[(String, File)] = - wrapNull(base.list()).collect { - case dir @ JavaHomeDir(_, m, n) => JavaVersion(nullBlank(m) + n).toString -> (base / dir) - } - } - - class MacOsDiscoverConfig extends JavaDiscoverConf { - val base: File = file("/Library") / "Java" / "JavaVirtualMachines" - - def javaHomes: Vector[(String, File)] = - wrapNull(base.list()).collect { - case dir @ JavaHomeDir(_, m, n) => - JavaVersion(nullBlank(m) + n).toString -> (base / dir / "Contents" / "Home") - } - } - - class WindowsDiscoverConfig extends JavaDiscoverConf { - val base: File = file("C://Program Files/Java") - - def javaHomes: Vector[(String, File)] = - wrapNull(base.list()).collect { - case dir @ JavaHomeDir(_, m, n) => JavaVersion(nullBlank(m) + n).toString -> (base / dir) - } - } - - // See https://github.com/shyiko/jabba - class JabbaDiscoverConfig extends JavaDiscoverConf { - val base: File = Path.userHome / ".jabba" / "jdk" - val JavaHomeDir = """([\w\-]+)\@(1\.)?([0-9]+).*""".r - - def javaHomes: Vector[(String, File)] = - wrapNull(base.list()).collect { - case dir @ JavaHomeDir(_, m, n) => - val v = JavaVersion(nullBlank(m) + n).toString - if ((base / dir / "Contents" / "Home").exists) v -> (base / dir / "Contents" / "Home") - else v -> (base / dir) - } - } - - class JavaHomeDiscoverConfig extends JavaDiscoverConf { - def javaHomes: Vector[(String, File)] = - sys.env - .get("JAVA_HOME") - .map(new java.io.File(_)) - .filter(_.exists()) - .flatMap { javaHome => - val base = javaHome.getParentFile - javaHome.getName match { - case dir @ JavaHomeDir(_, m, n) => Some(JavaVersion(nullBlank(m) + n).toString -> (base / dir)) - case _ => None - } - } - .toVector - } - - val configs = Vector( - new JabbaDiscoverConfig, - new LinuxDiscoverConfig(file("/usr") / "java"), - new LinuxDiscoverConfig(file("/usr") / "lib" / "jvm"), - new MacOsDiscoverConfig, - new WindowsDiscoverConfig, - new JavaHomeDiscoverConfig) - } - - def nullBlank(s: String): String = - if (s eq null) "" - else s - - // expand Java versions to 1-20 to 1.x, and vice versa to accept both "1.8" and "8" - private val oneDot = Map((1L to 20L).toVector.flatMap { i => - Vector(Vector(i) -> Vector(1L, i), Vector(1L, i) -> Vector(i)) - }: _*) - def expandJavaHomes(hs: Map[String, File]): Map[String, File] = - hs.flatMap { - case (k, v) => - val jv = JavaVersion(k) - if (oneDot.contains(jv.numbers)) - Vector(k -> v, jv.withNumbers(oneDot(jv.numbers)).toString -> v) - else Vector(k -> v) - } - - def wrapNull(a: Array[String]): Vector[String] = - if (a eq null) Vector() - else a.toVector - -} diff --git a/project/Doc.scala b/project/Doc.scala index 30198115af..fb41825479 100644 --- a/project/Doc.scala +++ b/project/Doc.scala @@ -134,7 +134,7 @@ object UnidocRoot extends AutoPlugin { val akkaSettings = UnidocRoot.CliOptions.genjavadocEnabled .ifTrue(Seq(javacOptions in (JavaUnidoc, unidoc) := { - if (JavaVersion.isJdk8) Seq("-Xdoclint:none") + if (JdkOptions.isJdk8) Seq("-Xdoclint:none") else Seq("-Xdoclint:none", "--frames", "--ignore-source-errors", "--no-module-directories") })) .getOrElse(Nil) @@ -170,7 +170,7 @@ object BootstrapGenjavadoc extends AutoPlugin { UnidocRoot.CliOptions.genjavadocEnabled .ifTrue { // require 11, fail fast for 8, 9, 10 - require(JavaVersion.isJdk11orHigher, "Javadoc generation requires at least jdk 11") + require(JdkOptions.isJdk11orHigher, "Javadoc generation requires at least jdk 11") sbtunidoc.GenJavadocPlugin } .getOrElse(plugins.JvmPlugin) diff --git a/project/Jdk9.scala b/project/Jdk9.scala index 1f84f779de..8ef5b49905 100644 --- a/project/Jdk9.scala +++ b/project/Jdk9.scala @@ -8,7 +8,7 @@ import sbt._ import sbt.Keys._ object Jdk9 extends AutoPlugin { - import JavaVersion.notOnJdk8 + import JdkOptions.notOnJdk8 lazy val CompileJdk9 = config("CompileJdk9").extend(Compile) diff --git a/project/JdkOptions.scala b/project/JdkOptions.scala new file mode 100644 index 0000000000..3563dcd786 --- /dev/null +++ b/project/JdkOptions.scala @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-2019 Lightbend Inc. + */ + +package akka + +import java.io.File + +import scala.annotation.tailrec +import scala.collection.immutable.ListMap +import sbt._ +import sbt.librarymanagement.SemanticSelector +import sbt.librarymanagement.VersionNumber + +object JdkOptions extends AutoPlugin { + object autoImport { + val targetSystemJdk = settingKey[Boolean]("Target the system JDK instead of building against JDK 8. When this is enabled resulting artifacts may not work on JDK 8!") + } + import autoImport._ + + val specificationVersion: String = sys.props("java.specification.version") + + val isJdk8: Boolean = + VersionNumber(specificationVersion).matchesSemVer(SemanticSelector(s"=1.8")) + val isJdk11orHigher: Boolean = + VersionNumber(specificationVersion).matchesSemVer(SemanticSelector(">=11")) + + def notOnJdk8[T](values: Seq[T]): Seq[T] = if (isJdk8) Seq.empty[T] else values + + def targetJdkScalacOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File]): Seq[String] = + selectOptions( + targetSystemJdk, + fullJavaHomes, + Seq("-target:jvm-1.8"), + // '-release 8' is not enough, for some reason we need the 8 rt.jar + // explicitly. To test whether this has the desired effect, compile + // akka-remote and check the invocation of 'ByteBuffer.clear()' in + // EnvelopeBuffer.class with 'javap -c': it should refer to + //""java/nio/ByteBuffer.clear:()Ljava/nio/Buffer" and not + // "java/nio/ByteBuffer.clear:()Ljava/nio/ByteBuffer". Issue #27079 + (java8home: File) => Seq("-release", "8", "-javabootclasspath", java8home + "/jre/lib/rt.jar") + ) + def targetJdkJavacOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File]): Seq[String] = + selectOptions( + targetSystemJdk, + fullJavaHomes, + Nil, + // '-release 8' would be a neater option here, but is currently not an + // option because it doesn't provide access to `sun.misc.Unsafe` #27079 + (java8home: File) => Seq("-source", "8", "-target", "8", "-bootclasspath", java8home + "/jre/lib/rt.jar") + ) + + private def selectOptions(targetSystemJdk: Boolean, fullJavaHomes: Map[String, File], jdk8options: Seq[String], jdk11options: File => Seq[String]): Seq[String] = + if (targetSystemJdk) + Nil + else if (isJdk8) + jdk8options + else fullJavaHomes.get("8") match { + case Some(java8home) => + jdk11options(java8home) + case None => + throw new MessageOnlyException("A JDK 8 installation was not found, but is required to build Akka. To target your system JDK, use the \"set every targetSystemJdk := true\" sbt command. Resulting artifacts may not work on JDK 8") + } + + val targetJdkSettings = Seq( + targetSystemJdk := false, + ) +}