Merge pull request #4139 from eed3si9n/wip/discover-java-home

Cross JDK forking
This commit is contained in:
eugene yokota 2018-05-30 13:45:00 -04:00 committed by GitHub
commit 2848770f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 536 additions and 19 deletions

View File

@ -6,6 +6,7 @@ cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot
- $HOME/.jabba
language: scala
@ -15,6 +16,14 @@ jdk:
matrix:
fast_finish: true
matrix:
include:
- env: SBT_CMD="scripted java/*"
before_install:
- curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.10.1/install.sh | bash && . ~/.jabba/jabba.sh
install:
- /home/travis/.jabba/bin/jabba install openjdk@1.10
env:
global:
- secure: d3bu2KNwsVHwfhbGgO+gmRfDKBJhfICdCJFGWKf2w3Gv86AJZX9nuTYRxz0KtdvEHO5Xw8WTBZLPb2thSJqhw9OCm4J8TBAVqCP0ruUj4+aqBUFy4bVexQ6WKE6nWHs4JPzPk8c6uC1LG3hMuzlC8RGETXtL/n81Ef1u7NjyXjs=
@ -26,7 +35,7 @@ env:
- SBT_CMD="scripted dependency-management/*2of4"
- SBT_CMD="scripted dependency-management/*3of4"
- SBT_CMD="scripted dependency-management/*4of4"
- SBT_CMD="scripted java/* package/* reporter/* run/* project-load/*"
- SBT_CMD="scripted package/* reporter/* run/* project-load/*"
- SBT_CMD="scripted project/*1of2"
- SBT_CMD="scripted project/*2of2"
- SBT_CMD="scripted source-dependencies/*1of3"

View File

@ -0,0 +1,40 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt
final class JavaVersion private (
val numbers: Vector[Long],
val vendor: Option[String]) extends Serializable {
def numberStr: String = numbers.mkString(".")
override def equals(o: Any): Boolean = o match {
case x: JavaVersion => (this.numbers == x.numbers) && (this.vendor == x.vendor)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.JavaVersion".##) + numbers.##) + vendor.##)
}
override def toString: String = {
vendor.map(_ + "@").getOrElse("") + numberStr
}
private[this] def copy(numbers: Vector[Long] = numbers, vendor: Option[String] = vendor): JavaVersion = {
new JavaVersion(numbers, vendor)
}
def withNumbers(numbers: Vector[Long]): JavaVersion = {
copy(numbers = numbers)
}
def withVendor(vendor: Option[String]): JavaVersion = {
copy(vendor = vendor)
}
def withVendor(vendor: String): JavaVersion = {
copy(vendor = Option(vendor))
}
}
object JavaVersion {
def apply(version: String): JavaVersion = sbt.internal.CrossJava.parseJavaVersion(version)
def apply(numbers: Vector[Long], vendor: Option[String]): JavaVersion = new JavaVersion(numbers, vendor)
def apply(numbers: Vector[Long], vendor: String): JavaVersion = new JavaVersion(numbers, Option(vendor))
}

View File

@ -17,3 +17,13 @@ enum PluginTrigger {
AllRequirements
NoTrigger
}
type JavaVersion {
numbers: [Long]
vendor: String
#x def numberStr: String = numbers.mkString(".")
#xtostring vendor.map(_ + "@").getOrElse("") + numberStr
#xcompanion def apply(version: String): JavaVersion = sbt.internal.CrossJava.parseJavaVersion(version)
}

View File

@ -99,14 +99,24 @@ object Cross {
}
/**
* Parse the given command into either an aggregate command or a command for a project
* Parse the given command into a list of aggregate projects and command to issue.
*/
private def parseCommand(command: String): Either[String, (String, String)] = {
private[sbt] def parseSlashCommand(
extracted: Extracted
)(command: String): (Seq[ProjectRef], String) = {
import extracted._
import DefaultParsers._
val parser = (OpOrID <~ charClass(_ == '/', "/")) ~ any.* map {
case project ~ cmd => (project, cmd.mkString)
case seg1 ~ cmd => (seg1, cmd.mkString)
}
Parser.parse(command, parser) match {
case Right((seg1, cmd)) =>
structure.allProjectRefs.find(_.project == seg1) match {
case Some(proj) => (Seq(proj), cmd)
case _ => (resolveAggregates(extracted), command)
}
case _ => (resolveAggregates(extracted), command)
}
Parser.parse(command, parser).left.map(_ => command)
}
def crossBuild: Command =
@ -115,12 +125,7 @@ object Cross {
private def crossBuildCommandImpl(state: State, args: CrossArgs): State = {
val x = Project.extract(state)
import x._
val (aggs, aggCommand) = parseCommand(args.command) match {
case Right((project, cmd)) =>
(structure.allProjectRefs.filter(_.project == project), cmd)
case Left(cmd) => (resolveAggregates(x), cmd)
}
val (aggs, aggCommand) = parseSlashCommand(x)(args.command)
val projCrossVersions = aggs map { proj =>
proj -> crossVersions(x, proj)
@ -229,14 +234,11 @@ object Cross {
args.command
} else {
args.command.map { rawCmd =>
parseCommand(rawCmd) match {
case Right(_) => rawCmd // A project is specified, run as is
case Left(cmd) =>
resolveAggregates(x)
.intersect(affectedRefs)
.collect { case ProjectRef(_, proj) => s"$proj/$cmd" }
.mkString("all ", " ", "")
}
val (aggs, aggCommand) = parseSlashCommand(x)(rawCmd)
aggs
.intersect(affectedRefs)
.map({ case ProjectRef(_, proj) => s"$proj/$aggCommand" })
.mkString("all ", " ", "")
}
}

View File

@ -69,6 +69,7 @@ import sbt.librarymanagement.syntax._
import sbt.util.InterfaceUtil.{ toJavaFunction => f1 }
import sbt.util._
import sbt.util.CacheImplicits._
import scala.collection.immutable.ListMap
import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal
import scala.xml.NodeSeq
@ -159,6 +160,9 @@ object Defaults extends BuildCommon {
scalaHome :== None,
apiURL := None,
javaHome :== None,
discoveredJavaHomes := CrossJava.discoverJavaHomes,
javaHomes :== ListMap.empty,
fullJavaHomes := CrossJava.expandJavaHomes(discoveredJavaHomes.value ++ javaHomes.value),
testForkedParallel :== false,
javaOptions :== Nil,
sbtPlugin :== false,

View File

@ -222,6 +222,7 @@ object Keys {
val scalaCompilerBridgeSource = settingKey[ModuleID]("Configures the module ID of the sources of the compiler bridge.").withRank(CSetting)
val scalaArtifacts = settingKey[Seq[String]]("Configures the list of artifacts which should match the Scala binary version").withRank(CSetting)
val enableBinaryCompileAnalysis = settingKey[Boolean]("Writes the analysis file in binary format")
val crossJavaVersions = settingKey[Seq[String]]("The java versions used during JDK cross testing").withRank(BPlusSetting)
val clean = taskKey[Unit]("Deletes files produced by the build, such as generated sources, compiled classes, and task caches.").withRank(APlusTask)
val console = taskKey[Unit]("Starts the Scala interpreter with the project classes on the classpath.").withRank(APlusTask)
@ -271,6 +272,10 @@ object Keys {
val outputStrategy = settingKey[Option[sbt.OutputStrategy]]("Selects how to log output when running a main class.").withRank(DSetting)
val connectInput = settingKey[Boolean]("If true, connects standard input when running a main class forked.").withRank(CSetting)
val javaHome = settingKey[Option[File]]("Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.").withRank(ASetting)
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.").withRank(CTask)
val javaOptions = taskKey[Seq[String]]("Options passed to a new JVM when forking.").withRank(BPlusTask)
val envVars = taskKey[Map[String, String]]("Environment variables used when forking a new JVM").withRank(BTask)

View File

@ -14,6 +14,7 @@ import sbt.internal.{
BuildUnit,
CommandExchange,
CommandStrings,
CrossJava,
DefaultBackgroundJobService,
EvaluateConfigurations,
Inspect,
@ -190,6 +191,8 @@ object BuiltinCommands {
oldLoadFailed,
Cross.crossBuild,
Cross.switchVersion,
CrossJava.switchJavaHome,
CrossJava.crossJavaHome,
PluginCross.pluginCross,
PluginCross.pluginSwitch,
Cross.crossRestoreSession,

View File

@ -415,4 +415,29 @@ $SwitchCommand [<scala-version>=]<scala-home>[!] [-v] [<command>]
See also `help $CrossCommand`
"""
val JavaCrossCommand = "java+"
val JavaSwitchCommand = "java++"
def javaCrossHelp: Help = Help.more(JavaCrossCommand, JavaCrossDetailed)
def javaSwitchHelp: Help = Help.more(JavaSwitchCommand, JavaSwitchDetailed)
def JavaCrossDetailed =
s"""$JavaCrossCommand <command>
Runs <command> for each JDK version specified for cross-JDK testing.
For each string in `crossJavaVersions` in the current project, this command sets the
`javaHome` of all projects to the corresponding Java home, reloads the build,
and executes <command>. When finished, it reloads the build with the original
`javaHome`.
Note that `Test / fork := true` is needed for `javaHome` to be effective.
See also `help $JavaSwitchCommand`
"""
def JavaSwitchDetailed =
s"""$JavaSwitchCommand <java-version>
Changes the JDK version and runs a command.
Sets the `javaHome` of all projects to <java-version> and
reloads the build. If <command> is provided, it is then executed.
See also `help $JavaSwitchCommand`
"""
}

View File

@ -0,0 +1,365 @@
/*
* sbt
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under BSD-3-Clause license (see LICENSE)
*/
package sbt
package internal
import java.io.File
import scala.collection.immutable.ListMap
import sbt.io.Path
import sbt.io.syntax._
import sbt.Cross._
import sbt.Def.{ ScopedKey, Setting }
import sbt.internal.util.complete.DefaultParsers._
import sbt.internal.util.AttributeKey
import sbt.internal.util.complete.{ DefaultParsers, Parser }
import sbt.internal.CommandStrings.{
JavaCrossCommand,
JavaSwitchCommand,
javaCrossHelp,
javaSwitchHelp
}
private[sbt] object CrossJava {
// 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 lookupJavaHome(jv: String, mappings: Map[String, File]): File = {
val ms = mappings map { case (k, v) => (JavaVersion(k), v) }
lookupJavaHome(JavaVersion(jv), ms)
}
def lookupJavaHome(jv: JavaVersion, mappings: Map[JavaVersion, File]): File = {
mappings.get(jv) match {
case Some(dir) => dir
// when looking for "10" it should match "openjdk@10"
case None if jv.vendor.isEmpty =>
val noVendors: Map[JavaVersion, File] = mappings map {
case (k, v) => k.withVendor(None) -> v
}
noVendors.get(jv).getOrElse(javaHomeNotFound(jv, mappings))
case _ => javaHomeNotFound(jv, mappings)
}
}
private def javaHomeNotFound(version: JavaVersion, mappings: Map[JavaVersion, File]): Nothing = {
sys.error(s"""Java home for $version was not found in $mappings
|
|use Global / javaHomes += JavaVersion("$version") -> file(...)""".stripMargin)
}
private case class SwitchTarget(version: Option[JavaVersion], home: Option[File], force: Boolean)
private case class SwitchJavaHome(target: SwitchTarget, verbose: Boolean, command: Option[String])
private def switchParser(state: State): Parser[SwitchJavaHome] = {
import DefaultParsers._
def versionAndCommand(spacePresent: Boolean) = {
val x = Project.extract(state)
import x._
val javaHomes = getJavaHomesTyped(x, currentRef)
val knownVersions = javaHomes.keysIterator.map(_.numberStr).toVector
val version: Parser[SwitchTarget] =
(token(
(StringBasic <~ "@").? ~ ((NatBasic) ~ ("." ~> NatBasic).*)
.examples(knownVersions: _*) ~ "!".?
) || token(StringBasic))
.map {
case Left(((vendor, (v1, vs)), bang)) =>
val force = bang.isDefined
val versionArg = (Vector(v1) ++ vs) map { _.toLong }
SwitchTarget(Option(JavaVersion(versionArg, vendor)), None, force)
case Right(home) =>
SwitchTarget(None, Option(new File(home)), true)
}
val spacedVersion =
if (spacePresent) version
else version & spacedFirst(JavaSwitchCommand)
val verbose = Parser.opt(token(Space ~> "-v"))
val optionalCommand = Parser.opt(token(Space ~> matched(state.combinedParser)))
(spacedVersion ~ verbose ~ optionalCommand).map {
case v ~ verbose ~ command =>
SwitchJavaHome(v, verbose.isDefined, command)
}
}
token(JavaSwitchCommand ~> OptSpace) flatMap { sp =>
versionAndCommand(sp.nonEmpty)
}
}
private def getJavaHomes(
extracted: Extracted,
proj: ResolvedReference
): Map[String, File] = {
import extracted._
(Keys.fullJavaHomes in proj get structure.data).get
}
private def getJavaHomesTyped(
extracted: Extracted,
proj: ResolvedReference
): Map[JavaVersion, File] = {
getJavaHomes(extracted, proj) map { case (k, v) => (JavaVersion(k), v) }
}
private def getCrossJavaVersions(
extracted: Extracted,
proj: ResolvedReference
): Seq[String] = {
import extracted._
import Keys._
(crossJavaVersions in proj get structure.data).getOrElse(Nil)
}
private def getCrossJavaHomes(extracted: Extracted, proj: ResolvedReference): Seq[File] = {
import extracted._
import Keys._
val fjh = (fullJavaHomes in proj get structure.data).get
(crossJavaVersions in proj get structure.data) map { jvs =>
jvs map { jv =>
lookupJavaHome(jv, fjh)
}
} getOrElse Vector()
}
private def switchCommandImpl(state: State, switch: SwitchJavaHome): State = {
val extracted = Project.extract(state)
import extracted._
import Keys.javaHome
// filter out subprojects based on switch target e.g. "10" vs what's in crossJavaVersions
// for the subproject. Only if crossJavaVersions is non-empty, and does NOT include "10"
// it will skip the subproject.
val projects: Seq[(ResolvedReference, Seq[String])] = {
val projectJavaVersions =
structure.allProjectRefs.map(proj => proj -> getCrossJavaVersions(extracted, proj))
if (switch.target.force) projectJavaVersions
else
switch.target.version match {
case None => projectJavaVersions
case Some(v) =>
projectJavaVersions flatMap {
case (proj, versions) =>
if (versions.isEmpty || versions.contains(v)) Vector(proj -> versions)
else Vector()
}
}
}
def setJavaHomeForProjects: State = {
val newSettings = projects.flatMap {
case (proj, javaVersions) =>
val fjh = getJavaHomesTyped(extracted, proj)
val home = switch.target match {
case SwitchTarget(Some(v), _, _) => lookupJavaHome(v, fjh)
case SwitchTarget(_, Some(h), _) => h
case _ => sys.error(s"unexpected ${switch.target}")
}
val scope = Scope(Select(proj), Zero, Zero, Zero)
Seq(
javaHome in scope := Some(home)
)
}
val filterKeys: Set[AttributeKey[_]] = Set(javaHome).map(_.key)
val projectsContains: Reference => Boolean = projects.map(_._1).toSet.contains
// Filter out any old javaHome version settings that were added, this is just for hygiene.
val filteredRawAppend = session.rawAppend.filter(_.key match {
case ScopedKey(Scope(Select(ref), Zero, Zero, Zero), key)
if filterKeys.contains(key) && projectsContains(ref) =>
false
case _ => true
})
val newSession = session.copy(rawAppend = filteredRawAppend ++ newSettings)
BuiltinCommands.reapply(newSession, structure, state)
}
setJavaHomeForProjects
}
def switchJavaHome: Command =
Command.arb(requireSession(switchParser), javaSwitchHelp)(switchCommandImpl)
def crossJavaHome: Command =
Command.arb(requireSession(crossParser), javaCrossHelp)(crossJavaHomeCommandImpl)
private case class CrossArgs(command: String, verbose: Boolean)
/**
* Parse the given command into either an aggregate command or a command for a project
*/
private def crossParser(state: State): Parser[CrossArgs] =
token(JavaCrossCommand <~ OptSpace) flatMap { _ =>
(token(Parser.opt("-v" <~ Space)) ~ token(matched(state.combinedParser))).map {
case (verbose, command) => CrossArgs(command, verbose.isDefined)
} & spacedFirst(JavaCrossCommand)
}
private def crossJavaHomeCommandImpl(state: State, args: CrossArgs): State = {
val x = Project.extract(state)
import x._
val (aggs, aggCommand) = Cross.parseSlashCommand(x)(args.command)
val projCrossVersions = aggs map { proj =>
proj -> getCrossJavaHomes(x, proj)
}
// if we support javaHome, projVersions should be cached somewhere since
// running ++2.11.1 is at the root level is going to mess with the scalaVersion for the aggregated subproj
val projVersions = (projCrossVersions flatMap {
case (proj, versions) => versions map { proj.project -> _ }
}).toList
val verbose = ""
// println(s"projVersions $projVersions")
if (projVersions.isEmpty) {
state
} else {
// Detect whether a task or command has been issued
val allCommands = Parser.parse(aggCommand, Act.aggregatedKeyParser(x)) match {
case Left(_) =>
// It's definitely not a task, check if it's a valid command, because we don't want to emit the warning
// message below for typos.
val validCommand = Parser.parse(aggCommand, state.combinedParser).isRight
val distinctCrossConfigs = projCrossVersions.map(_._2.toSet).distinct
if (validCommand && distinctCrossConfigs.size > 1) {
state.log.warn(
"Issuing a Java cross building command, but not all sub projects have the same cross build " +
"configuration. This could result in subprojects cross building against Java versions that they are " +
"not compatible with. Try issuing cross building command with tasks instead, since sbt will be able " +
"to ensure that cross building is only done using configured project and Java version combinations " +
"that are configured."
)
state.log.debug("Java versions configuration is:")
projCrossVersions.foreach {
case (project, versions) => state.log.debug(s"$project: $versions")
}
}
// Execute using a blanket switch
projCrossVersions.toMap.apply(currentRef).flatMap { version =>
// Force scala version
Seq(s"$JavaSwitchCommand $verbose $version!", aggCommand)
}
case Right(_) =>
// We have a key, we're likely to be able to cross build this using the per project behaviour.
// Group all the projects by scala version
projVersions.groupBy(_._2).mapValues(_.map(_._1)).toSeq.flatMap {
case (version, Seq(project)) =>
// If only one project for a version, issue it directly
Seq(s"$JavaSwitchCommand $verbose $version", s"$project/$aggCommand")
case (version, projects) if aggCommand.contains(" ") =>
// If the command contains a space, then the `all` command won't work because it doesn't support issuing
// commands with spaces, so revert to running the command on each project one at a time
s"$JavaSwitchCommand $verbose $version" :: projects
.map(project => s"$project/$aggCommand")
case (version, projects) =>
// First switch scala version, then use the all command to run the command on each project concurrently
Seq(
s"$JavaSwitchCommand $verbose $version",
projects.map(_ + "/" + aggCommand).mkString("all ", " ", "")
)
}
}
allCommands.toList ::: captureCurrentSession(state, x)
}
}
private val JavaCapturedSession = AttributeKey[Seq[Setting[_]]]("javaCrossCapturedSession")
private def captureCurrentSession(state: State, extracted: Extracted): State = {
state.put(JavaCapturedSession, extracted.session.rawAppend)
}
def discoverJavaHomes: ListMap[String, File] = {
import JavaDiscoverConfig._
val configs = Vector(jabba, linux, macOS)
ListMap(configs flatMap { _.javaHomes }: _*)
}
sealed trait JavaDiscoverConf {
def javaHomes: Vector[(String, File)]
}
object JavaDiscoverConfig {
val linux = new JavaDiscoverConf {
val base: File = file("/usr") / "lib" / "jvm"
val JavaHomeDir = """java-([0-9]+)-.*""".r
def javaHomes: Vector[(String, File)] =
wrapNull(base.list()).collect {
case dir @ JavaHomeDir(ver) => JavaVersion(ver).toString -> (base / dir)
}
}
val macOS = new JavaDiscoverConf {
val base: File = file("/Library") / "Java" / "JavaVirtualMachines"
val JavaHomeDir = """jdk-?(1\.)?([0-9]+).*""".r
def javaHomes: Vector[(String, File)] =
wrapNull(base.list()).collect {
case dir @ JavaHomeDir(m, n) =>
JavaVersion(nullBlank(m) + n).toString -> (base / dir / "Contents" / "Home")
}
}
// See https://github.com/shyiko/jabba
val jabba = new 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(vendor, m, n) =>
val v = JavaVersion(nullBlank(m) + n).withVendor(vendor).toString
if ((base / dir / "Contents" / "Home").exists) v -> (base / dir / "Contents" / "Home")
else v -> (base / dir)
}
}
}
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
}

View File

@ -0,0 +1,16 @@
package pkg
import java.nio.file.{ Paths, Files }
import java.nio.charset.Charset
object A extends App {
val out = Paths.get("out.txt")
val content = sys.props("java.version")
val w = Files.newBufferedWriter(out, Charset.forName("UTF-8"))
try {
w.write(content)
w.flush()
} finally {
w.close
}
}

View File

@ -0,0 +1,25 @@
import complete.DefaultParsers._
val check = inputKey[Unit]("Runs the check")
lazy val root = (project in file("."))
.settings(
ThisBuild / scalaVersion := "2.12.6",
crossJavaVersions := List("1.8"),
// read out.txt and see if it starts with the passed in number
check := {
val arg1: Int = (Space ~> NatBasic).parsed
file("out.txt") match {
case out if out.exists =>
IO.readLines(out).headOption match {
case Some(v) if v startsWith arg1.toString => ()
case Some(v) if v startsWith s"1.$arg1" => ()
case x => sys.error(s"unexpected value: $x")
}
case out => sys.error(s"$out doesn't exist")
}
},
Compile / run / fork := true,
)

View File

@ -0,0 +1,6 @@
> java+ run
> check 8
> java++ 10!
> run
> check 10

View File

@ -0,0 +1,5 @@
Global / javaHomes += "6" -> file("/good/old/times/java-6")
TaskKey[Unit]("check") := {
assert(fullJavaHomes.value("1.6").getAbsolutePath.contains("java-6"))
}

View File

@ -0,0 +1 @@
> check

View File

@ -206,6 +206,7 @@ final class ScriptedTests(
case "dependency-management/update-sbt-classifiers" => LauncherBased // tbd
case "dependency-management/url" => LauncherBased // tbd
case "java/argfile" => LauncherBased // sbt/Package$
case "java/cross" => LauncherBased // sbt/Package$
case "java/basic" => LauncherBased // sbt/Package$
case "java/varargs-main" => LauncherBased // sbt/Package$
case "package/lazy-name" => LauncherBased // sbt/Package$