mirror of https://github.com/sbt/sbt.git
Merge pull request #8493 from eed3si9n/bport/launcher-package
[1.x] bport: Upgrade launcher-package to sbt 1.10.7
This commit is contained in:
commit
b352eb1ff6
|
|
@ -58,10 +58,9 @@ jobs:
|
|||
./client/target/bin/sbtn --sbt-script=$(pwd)/sbt shutdown
|
||||
# test launcher script
|
||||
echo build using JDK 8 test using JDK 8 and JDK 25
|
||||
cd launcher-package
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER rpm:packageBin debian:packageBin
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test
|
||||
cd citest && ./test.sh
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER launcherPackage/Rpm/packageBin launcherPackage/Debian/packageBin
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER launcherPackageIntegrationTest/test
|
||||
cd launcher-package/citest && ./test.sh
|
||||
JAVA_HOME="$JAVA_HOME_25_X64"
|
||||
PATH="$JAVA_HOME_25_X64/bin:$PATH"
|
||||
java -Xmx32m -version
|
||||
|
|
@ -73,9 +72,8 @@ jobs:
|
|||
# test building sbtn on macOS
|
||||
./sbt "-Dsbt.io.virtual=false" nativeImage
|
||||
# test launcher script
|
||||
cd launcher-package
|
||||
bin/coursier resolve
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test
|
||||
launcher-package/bin/coursier resolve
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER launcherPackageIntegrationTest/test
|
||||
# This fails due to the JLine issue
|
||||
# cd citest && ./test.sh
|
||||
- name: Client test (Windows)
|
||||
|
|
@ -89,9 +87,8 @@ jobs:
|
|||
./client/target/bin/sbtn --sbt-script=$(pwd)/launcher-package/src/universal/bin/sbt.bat shutdown
|
||||
# test launcher script
|
||||
echo build using JDK 8 test using JDK 8 and JDK 25
|
||||
cd launcher-package
|
||||
bin/coursier.bat resolve
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test
|
||||
cd citest
|
||||
launcher-package/bin/coursier.bat resolve
|
||||
sbt -Dsbt.build.version=$TEST_SBT_VER launcherPackageIntegrationTest/test
|
||||
cd launcher-package/citest
|
||||
./test.bat
|
||||
test3/test3.bat
|
||||
|
|
|
|||
|
|
@ -24,5 +24,6 @@ trailingCommas = preserve
|
|||
|
||||
# TODO update scalafmt and enable Scala 3
|
||||
project.excludeFilters = [
|
||||
"internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala"
|
||||
"internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala",
|
||||
"launcher-package/integration-test/src/test/scala/*",
|
||||
]
|
||||
|
|
|
|||
31
build.sbt
31
build.sbt
|
|
@ -54,6 +54,8 @@ Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty)
|
|||
Global / excludeLint += componentID
|
||||
Global / excludeLint += scriptedBufferLog
|
||||
Global / excludeLint += checkPluginCross
|
||||
Global / excludeLint += nativeImageJvm
|
||||
Global / excludeLint += nativeImageVersion
|
||||
|
||||
def commonBaseSettings: Seq[Setting[_]] = Def.settings(
|
||||
headerLicense := Some(
|
||||
|
|
@ -1508,3 +1510,32 @@ ThisBuild / publishTo := {
|
|||
else localStaging.value
|
||||
}
|
||||
ThisBuild / publishMavenStyle := true
|
||||
|
||||
lazy val launcherPackage = (project in file("launcher-package"))
|
||||
lazy val launcherPackageIntegrationTest =
|
||||
(project in (file("launcher-package") / "integration-test"))
|
||||
.settings(
|
||||
name := "integration-test",
|
||||
scalaVersion := scala3,
|
||||
libraryDependencies ++= Seq(
|
||||
scalaVerify % Test,
|
||||
hedgehog % Test,
|
||||
// This needs to be hardcoded here, and not use addSbtIO
|
||||
"org.scala-sbt" %% "io" % "1.10.5" % Test,
|
||||
),
|
||||
testFrameworks += TestFramework("hedgehog.sbt.Framework"),
|
||||
testFrameworks += TestFramework("verify.runner.Framework"),
|
||||
Test / test := {
|
||||
(Test / test)
|
||||
.dependsOn(launcherPackage / Universal / packageBin)
|
||||
.dependsOn(launcherPackage / Universal / stage)
|
||||
.value
|
||||
},
|
||||
Test / testOnly := {
|
||||
(Test / testOnly)
|
||||
.dependsOn(launcherPackage / Universal / packageBin)
|
||||
.dependsOn(launcherPackage / Universal / stage)
|
||||
.evaluated
|
||||
},
|
||||
Test / parallelExecution := false
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import scala.util.control.Exception.catching
|
||||
import NativePackagerHelper._
|
||||
import com.typesafe.sbt.packager.SettingsHelper._
|
||||
import DebianConstants._
|
||||
import scala.sys.process.*
|
||||
import NativePackagerHelper.*
|
||||
import com.typesafe.sbt.packager.SettingsHelper.*
|
||||
import DebianConstants.*
|
||||
import Dependencies.*
|
||||
|
||||
lazy val sbtOfflineInstall =
|
||||
sys.props.getOrElse("sbt.build.offline", sys.env.getOrElse("sbt.build.offline", "false")) match {
|
||||
|
|
@ -21,12 +23,10 @@ lazy val sbtIncludeSbtLaunch =
|
|||
case "false" | "0" => false
|
||||
case _ => false
|
||||
}
|
||||
lazy val sbtVersionToRelease = sys.props.getOrElse("sbt.build.version", sys.env.getOrElse("sbt.build.version", {
|
||||
sys.error("-Dsbt.build.version must be set")
|
||||
}))
|
||||
lazy val sbtVersionToRelease = sys.props
|
||||
.getOrElse("sbt.build.version", sys.env.getOrElse("sbt.build.version", "1.12.0"))
|
||||
|
||||
lazy val scala210 = "2.10.7"
|
||||
lazy val scala212 = "2.12.21"
|
||||
lazy val scala210Jline = "org.scala-lang" % "jline" % scala210
|
||||
lazy val jansi = {
|
||||
if (sbtVersionToRelease startsWith "1.") "org.fusesource.jansi" % "jansi" % "1.12"
|
||||
|
|
@ -69,6 +69,8 @@ val debianBuildId = settingKey[Int]("build id for Debian")
|
|||
|
||||
val exportRepoUsingCoursier = taskKey[File]("export Maven style repository")
|
||||
val exportRepoCsrDirectory = settingKey[File]("")
|
||||
val exportRepo = taskKey[File]("export Ivy style repository")
|
||||
val exportRepoDirectory = settingKey[File]("directory for exported repository")
|
||||
|
||||
val universalMacPlatform = "universal-apple-darwin"
|
||||
val x86LinuxPlatform = "x86_64-pc-linux"
|
||||
|
|
@ -79,11 +81,10 @@ val x86LinuxImageName = s"sbtn-$x86LinuxPlatform"
|
|||
val aarch64LinuxImageName = s"sbtn-$aarch64LinuxPlatform"
|
||||
val x86WindowsImageName = s"sbtn-$x86WindowsPlatform.exe"
|
||||
|
||||
organization in ThisBuild := "org.scala-sbt"
|
||||
version in ThisBuild := "0.1.0"
|
||||
Global / excludeLintKeys += bintrayGenericPackagesUrl
|
||||
|
||||
// This build creates a SBT plugin with handy features *and* bundles the SBT script for distribution.
|
||||
val root = (project in file(".")).
|
||||
val launcherPackage = (project in file(".")).
|
||||
enablePlugins(UniversalPlugin, LinuxPlugin, DebianPlugin, RpmPlugin, WindowsPlugin,
|
||||
UniversalDeployPlugin, RpmDeployPlugin, WindowsDeployPlugin).
|
||||
settings(
|
||||
|
|
@ -91,7 +92,7 @@ val root = (project in file(".")).
|
|||
packageName := "sbt",
|
||||
crossTarget := target.value,
|
||||
clean := {
|
||||
val _ = (clean in dist).value
|
||||
val _ = (dist / clean).value
|
||||
clean.value
|
||||
},
|
||||
credentials ++= {
|
||||
|
|
@ -109,13 +110,24 @@ val root = (project in file(".")).
|
|||
sbtLaunchJar := {
|
||||
val uri = sbtLaunchJarUrl.value
|
||||
val file = sbtLaunchJarLocation.value
|
||||
import dispatch.classic._
|
||||
if(!file.exists) {
|
||||
// oddly, some places require us to create the file before writing...
|
||||
IO.touch(file)
|
||||
val url = new java.net.URL(uri)
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(file))
|
||||
try Http(url(uri) >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
}
|
||||
// TODO - GPG Trust validation.
|
||||
file
|
||||
|
|
@ -135,12 +147,23 @@ val root = (project in file(".")).
|
|||
val linuxX86Tar = t / linuxX86ImageTar
|
||||
val linuxAarch64Tar = t / linuxAarch64ImageTar
|
||||
val windowsZip = t / windowsImageZip
|
||||
import dispatch.classic._
|
||||
if(!macosUniversalTar.exists && !isWindows && sbtIncludeSbtn) {
|
||||
IO.touch(macosUniversalTar)
|
||||
val url = new java.net.URL(s"$baseUrl/v$v/$macosUniversalImageTar")
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(macosUniversalTar))
|
||||
try Http(url(s"$baseUrl/v$v/$macosUniversalImageTar") >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
val platformDir = t / universalMacPlatform
|
||||
IO.createDirectory(platformDir)
|
||||
s"tar zxvf $macosUniversalTar --directory $platformDir".!
|
||||
|
|
@ -148,9 +171,21 @@ val root = (project in file(".")).
|
|||
}
|
||||
if(!linuxX86Tar.exists && !isWindows && sbtIncludeSbtn) {
|
||||
IO.touch(linuxX86Tar)
|
||||
val url = new java.net.URL(s"$baseUrl/v$v/$linuxX86ImageTar")
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(linuxX86Tar))
|
||||
try Http(url(s"$baseUrl/v$v/$linuxX86ImageTar") >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
val platformDir = t / x86LinuxPlatform
|
||||
IO.createDirectory(platformDir)
|
||||
s"""tar zxvf $linuxX86Tar --directory $platformDir""".!
|
||||
|
|
@ -158,9 +193,21 @@ val root = (project in file(".")).
|
|||
}
|
||||
if(!linuxAarch64Tar.exists && !isWindows && sbtIncludeSbtn) {
|
||||
IO.touch(linuxAarch64Tar)
|
||||
val url = new java.net.URL(s"$baseUrl/v$v/$linuxAarch64ImageTar")
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(linuxAarch64Tar))
|
||||
try Http(url(s"$baseUrl/v$v/$linuxAarch64ImageTar") >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
val platformDir = t / aarch64LinuxPlatform
|
||||
IO.createDirectory(platformDir)
|
||||
s"""tar zxvf $linuxAarch64Tar --directory $platformDir""".!
|
||||
|
|
@ -168,9 +215,21 @@ val root = (project in file(".")).
|
|||
}
|
||||
if(!windowsZip.exists && sbtIncludeSbtn) {
|
||||
IO.touch(windowsZip)
|
||||
val url = new java.net.URL(s"$baseUrl/v$v/$windowsImageZip")
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(windowsZip))
|
||||
try Http(url(s"$baseUrl/v$v/$windowsImageZip") >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
val platformDir = t / x86WindowsPlatform
|
||||
IO.unzip(windowsZip, platformDir)
|
||||
IO.move(platformDir / "sbtn.exe", t / x86WindowsImageName)
|
||||
|
|
@ -200,44 +259,44 @@ val root = (project in file(".")).
|
|||
|
||||
// DEBIAN SPECIFIC
|
||||
debianBuildId := sys.props.getOrElse("sbt.build.patch", sys.env.getOrElse("DIST_PATCHVER", "0")).toInt,
|
||||
version in Debian := {
|
||||
Debian / version := {
|
||||
if (debianBuildId.value == 0) sbtVersionToRelease
|
||||
else sbtVersionToRelease + "." + debianBuildId.value
|
||||
},
|
||||
// Used to have "openjdk-8-jdk" but that doesn't work on Ubuntu 14.04 https://github.com/sbt/sbt/issues/3105
|
||||
// before that we had java6-runtime-headless" and that was pulling in JDK9 on Ubuntu 16.04 https://github.com/sbt/sbt/issues/2931
|
||||
debianPackageDependencies in Debian ++= Seq("bash (>= 3.2)"),
|
||||
debianPackageRecommends in Debian += "git",
|
||||
linuxPackageMappings in Debian += {
|
||||
Debian / debianPackageDependencies ++= Seq("bash (>= 3.2)", "curl | wget"),
|
||||
Debian / debianPackageRecommends += "git",
|
||||
Debian / linuxPackageMappings += {
|
||||
val bd = sourceDirectory.value
|
||||
(packageMapping(
|
||||
(bd / "debian" / "changelog") -> "/usr/share/doc/sbt/changelog.gz"
|
||||
) withUser "root" withGroup "root" withPerms "0644" gzipped) asDocs()
|
||||
},
|
||||
debianChangelog in Debian := { Some(sourceDirectory.value / "debian" / "changelog") },
|
||||
addPackage(Debian, packageBin in Debian, "deb"),
|
||||
debianNativeBuildOptions in Debian := Seq("-Zgzip", "-z3"),
|
||||
Debian / debianChangelog := { Some(sourceDirectory.value / "debian" / "changelog") },
|
||||
addPackage(Debian, (Debian / packageBin), "deb"),
|
||||
Debian / debianNativeBuildOptions := Seq("-Zgzip", "-z3"),
|
||||
|
||||
// use the following instead of DebianDeployPlugin to skip changelog
|
||||
makeDeploymentSettings(Debian, packageBin in Debian, "deb"),
|
||||
makeDeploymentSettings(Debian, (Debian / packageBin), "deb"),
|
||||
|
||||
// RPM SPECIFIC
|
||||
rpmRelease := debianBuildId.value.toString,
|
||||
version in Rpm := {
|
||||
Rpm / version := {
|
||||
val stable0 = (sbtVersionToRelease split "[^\\d]" filterNot (_.isEmpty) mkString ".")
|
||||
val stable = if (rpmRelease.value == "0") stable0
|
||||
else stable0 + "." + rpmRelease.value
|
||||
if (isExperimental) ((sbtVersionToRelease split "[^\\d]" filterNot (_.isEmpty)).toList match {
|
||||
case List(a, b, c, d) => List(0, 99, c, d).mkString(".")
|
||||
case List(_, _, c, d) => List(0, 99, c, d).mkString(".")
|
||||
})
|
||||
else stable
|
||||
},
|
||||
// remove sbtn from RPM because it complains about it being noarch
|
||||
linuxPackageMappings in Rpm := {
|
||||
val orig = (linuxPackageMappings in Rpm).value
|
||||
val nativeMappings = sbtnJarsMappings.value
|
||||
Rpm / linuxPackageMappings := {
|
||||
val orig = ((Rpm / linuxPackageMappings)).value
|
||||
val _ = sbtnJarsMappings.value
|
||||
orig.map(o => o.copy(mappings = o.mappings.toList filterNot {
|
||||
case (x, p) => p.contains("sbtn-x86_64") || p.contains("sbtn-aarch64")
|
||||
case (_, p) => p.contains("sbtn-x86_64") || p.contains("sbtn-aarch64")
|
||||
}))
|
||||
},
|
||||
rpmVendor := "scalacenter",
|
||||
|
|
@ -252,7 +311,7 @@ val root = (project in file(".")).
|
|||
|
||||
// WINDOWS SPECIFIC
|
||||
windowsBuildId := 0,
|
||||
version in Windows := {
|
||||
Windows / version := {
|
||||
val bid = windowsBuildId.value
|
||||
val sv = sbtVersionToRelease
|
||||
(sv split "[^\\d]" filterNot (_.isEmpty)) match {
|
||||
|
|
@ -262,26 +321,26 @@ val root = (project in file(".")).
|
|||
case Array(major) => Seq(major, "0", "0", bid.toString) mkString "."
|
||||
}
|
||||
},
|
||||
maintainer in Windows := "Scala Center",
|
||||
packageSummary in Windows := "sbt " + (version in Windows).value,
|
||||
packageDescription in Windows := "The interactive build tool.",
|
||||
Windows / maintainer := "Scala Center",
|
||||
Windows / packageSummary := "sbt " + (Windows / version).value,
|
||||
Windows / packageDescription := "The interactive build tool.",
|
||||
wixProductId := "ce07be71-510d-414a-92d4-dff47631848a",
|
||||
wixProductUpgradeId := Hash.toHex(Hash((version in Windows).value)).take(32),
|
||||
wixProductUpgradeId := Hash.toHex(Hash((Windows / version).value)).take(32),
|
||||
javacOptions := Seq("-source", "1.8", "-target", "1.8"),
|
||||
|
||||
// Universal ZIP download install.
|
||||
packageName in Universal := packageName.value, // needs to be set explicitly due to a bug in native-packager
|
||||
name in Windows := packageName.value,
|
||||
packageName in Windows := packageName.value,
|
||||
version in Universal := sbtVersionToRelease,
|
||||
Universal / packageName := packageName.value, // needs to be set explicitly due to a bug in native-packager
|
||||
Windows / name := packageName.value,
|
||||
Windows / packageName := packageName.value,
|
||||
Universal / version := sbtVersionToRelease,
|
||||
|
||||
mappings in Universal += {
|
||||
Universal / mappings += {
|
||||
(baseDirectory.value.getParentFile / "sbt") -> ("bin" + java.io.File.separator + "sbt")
|
||||
},
|
||||
|
||||
mappings in Universal := {
|
||||
val t = (target in Universal).value
|
||||
val prev = (mappings in Universal).value
|
||||
Universal / mappings := {
|
||||
val t = (Universal / target).value
|
||||
val prev = (Universal / mappings).value
|
||||
val BinSbt = "bin" + java.io.File.separator + "sbt"
|
||||
val BinBat = BinSbt + ".bat"
|
||||
prev.toList map {
|
||||
|
|
@ -307,7 +366,7 @@ val root = (project in file(".")).
|
|||
case (k, v) => (k, v)
|
||||
}
|
||||
},
|
||||
mappings in Universal ++= (Def.taskDyn {
|
||||
Universal / mappings ++= (Def.taskDyn {
|
||||
if (sbtIncludeSbtLaunch)
|
||||
Def.task {
|
||||
Seq(
|
||||
|
|
@ -316,65 +375,46 @@ val root = (project in file(".")).
|
|||
}
|
||||
else Def.task { Seq[(File, String)]() }
|
||||
}).value,
|
||||
mappings in Universal ++= sbtnJarsMappings.value,
|
||||
mappings in Universal ++= (Def.taskDyn {
|
||||
Universal / mappings ++= sbtnJarsMappings.value,
|
||||
Universal / mappings ++= (Def.taskDyn {
|
||||
if (sbtOfflineInstall && sbtVersionToRelease.startsWith("1."))
|
||||
Def.task {
|
||||
val _ = (exportRepoUsingCoursier in dist).value
|
||||
directory((target in dist).value / "lib")
|
||||
val _ = ((dist / exportRepoUsingCoursier)).value
|
||||
directory(((dist / target)).value / "lib")
|
||||
}
|
||||
else if (sbtOfflineInstall)
|
||||
Def.task {
|
||||
val _ = (exportRepo in dist).value
|
||||
directory((target in dist).value / "lib")
|
||||
val _ = ((dist / exportRepo)).value
|
||||
directory(((dist / target)).value / "lib")
|
||||
}
|
||||
else Def.task { Seq[(File, String)]() }
|
||||
}).value,
|
||||
mappings in Universal ++= {
|
||||
Universal / mappings ++= {
|
||||
val base = baseDirectory.value
|
||||
if (sbtVersionToRelease startsWith "0.13.") Nil
|
||||
else Seq[(File, String)](base.getParentFile / "LICENSE" -> "LICENSE", base / "NOTICE" -> "NOTICE")
|
||||
},
|
||||
|
||||
// Misccelaneous publishing stuff...
|
||||
projectID in Debian := {
|
||||
// Miscellaneous publishing stuff...
|
||||
Debian / projectID := {
|
||||
val m = moduleID.value
|
||||
m.copy(revision = (version in Debian).value)
|
||||
m.withRevision((Debian / version).value)
|
||||
},
|
||||
projectID in Windows := {
|
||||
Windows / projectID := {
|
||||
val m = moduleID.value
|
||||
m.copy(revision = (version in Windows).value)
|
||||
m.withRevision((Windows / version).value)
|
||||
},
|
||||
projectID in Rpm := {
|
||||
Rpm / projectID := {
|
||||
val m = moduleID.value
|
||||
m.copy(revision = (version in Rpm).value)
|
||||
m.withRevision((Rpm / version).value)
|
||||
},
|
||||
projectID in Universal := {
|
||||
Universal / projectID := {
|
||||
val m = moduleID.value
|
||||
m.copy(revision = (version in Universal).value)
|
||||
m.withRevision((Universal / version).value)
|
||||
}
|
||||
)
|
||||
|
||||
lazy val integrationTest = (project in file("integration-test"))
|
||||
.settings(
|
||||
name := "integration-test",
|
||||
scalaVersion := scala212,
|
||||
libraryDependencies ++= Seq(
|
||||
"io.monix" %% "minitest" % "2.3.2" % Test,
|
||||
"com.eed3si9n.expecty" %% "expecty" % "0.11.0" % Test,
|
||||
"org.scala-sbt" %% "io" % "1.10.5" % Test
|
||||
),
|
||||
testFrameworks += new TestFramework("minitest.runner.Framework"),
|
||||
test in Test := {
|
||||
(test in Test).dependsOn(((packageBin in Universal) in LocalRootProject).dependsOn(((stage in (Universal) in LocalRootProject)))).value
|
||||
},
|
||||
testOnly in Test := {
|
||||
(testOnly in Test).dependsOn(((packageBin in Universal) in LocalRootProject).dependsOn(((stage in (Universal) in LocalRootProject)))).evaluated
|
||||
},
|
||||
parallelExecution in Test := false
|
||||
)
|
||||
|
||||
def downloadUrlForVersion(v: String) = (v split "[^\\d]" flatMap (i => catching(classOf[Exception]) opt (i.toInt))) match {
|
||||
def downloadUrlForVersion(v: String) = (v.split("[^\\d]") flatMap (i => catching(classOf[Exception]) opt (i.toInt))) match {
|
||||
case Array(0, 11, 3, _*) => "https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.11.3-2/sbt-launch.jar"
|
||||
case Array(0, 11, x, _*) if x >= 3 => "https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/"+v+"/sbt-launch.jar"
|
||||
case Array(0, y, _*) if y >= 12 => "https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/"+v+"/sbt-launch.jar"
|
||||
|
|
@ -383,8 +423,6 @@ def downloadUrlForVersion(v: String) = (v split "[^\\d]" flatMap (i => catching(
|
|||
}
|
||||
|
||||
def makePublishToForConfig(config: Configuration) = {
|
||||
val v = sbtVersionToRelease
|
||||
|
||||
// Add the publish to and ensure global resolvers has the resolver we just configured.
|
||||
inConfig(config)(Seq(
|
||||
name := "sbt",
|
||||
|
|
@ -406,9 +444,7 @@ def makePublishToForConfig(config: Configuration) = {
|
|||
val resolver = Resolver.url(id, new URL(url))(Patterns(pattern))
|
||||
Some(resolver)
|
||||
}
|
||||
)) ++ Seq(
|
||||
resolvers ++= ((publishTo in config) apply (_.toSeq)).value
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
def publishToSettings =
|
||||
|
|
@ -416,19 +452,29 @@ def publishToSettings =
|
|||
|
||||
def downloadUrl(uri: URI, out: File): Unit =
|
||||
{
|
||||
import dispatch.classic._
|
||||
if(!out.exists) {
|
||||
IO.touch(out)
|
||||
val url = new java.net.URL(uri.toString)
|
||||
val connection = url.openConnection()
|
||||
val input = connection.getInputStream
|
||||
val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(out))
|
||||
try Http(url(uri.toString) >>> writer)
|
||||
finally writer.close()
|
||||
try {
|
||||
val buffer = new Array[Byte](8192)
|
||||
var bytesRead = input.read(buffer)
|
||||
while (bytesRead != -1) {
|
||||
writer.write(buffer, 0, bytesRead)
|
||||
bytesRead = input.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
input.close()
|
||||
writer.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def colonName(m: ModuleID): String = s"${m.organization}:${m.name}:${m.revision}"
|
||||
|
||||
lazy val dist = (project in file("dist"))
|
||||
.enablePlugins(ExportRepoPlugin)
|
||||
.settings(
|
||||
name := "dist",
|
||||
scalaVersion := {
|
||||
|
|
@ -437,27 +483,27 @@ lazy val dist = (project in file("dist"))
|
|||
},
|
||||
libraryDependencies ++= Seq(sbtActual, jansi, scala212Compiler, scala212Jline, scala212Xml) ++ sbt013ExtraDeps,
|
||||
exportRepo := {
|
||||
val old = exportRepo.value
|
||||
val outDir = exportRepoDirectory.value
|
||||
sbtVersionToRelease match {
|
||||
case v if v.startsWith("1.") =>
|
||||
sys.error("sbt 1.x should use coursier")
|
||||
case v if v.startsWith("0.13.") =>
|
||||
val outbase = exportRepoDirectory.value / "org.scala-sbt" / "compiler-interface" / v
|
||||
val outbase = outDir / "org.scala-sbt" / "compiler-interface" / v
|
||||
val uribase = s"https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/compiler-interface/$v/"
|
||||
downloadUrl(uri(uribase + "ivys/ivy.xml"), outbase / "ivys" / "ivy.xml")
|
||||
downloadUrl(uri(uribase + "jars/compiler-interface.jar"), outbase / "jars" / "compiler-interface.jar")
|
||||
downloadUrl(uri(uribase + "srcs/compiler-interface-sources.jar"), outbase / "srcs" / "compiler-interface-sources.jar")
|
||||
case _ =>
|
||||
}
|
||||
old
|
||||
outDir
|
||||
},
|
||||
exportRepoDirectory := target.value / "lib" / "local-preloaded",
|
||||
exportRepoCsrDirectory := exportRepoDirectory.value,
|
||||
exportRepoUsingCoursier := {
|
||||
val outDirectory = exportRepoCsrDirectory.value
|
||||
val csr =
|
||||
if (isWindows) (baseDirectory in LocalRootProject).value / "bin" / "coursier.bat"
|
||||
else (baseDirectory in LocalRootProject).value / "bin" / "coursier"
|
||||
if (isWindows) (LocalRootProject / baseDirectory).value / "bin" / "coursier.bat"
|
||||
else (LocalRootProject / baseDirectory).value / "bin" / "coursier"
|
||||
val cache = target.value / "coursier"
|
||||
IO.delete(cache)
|
||||
val v = sbtVersionToRelease
|
||||
|
|
@ -481,7 +527,7 @@ lazy val dist = (project in file("dist"))
|
|||
outDirectory
|
||||
},
|
||||
conflictWarning := ConflictWarning.disable,
|
||||
publish := (),
|
||||
publishLocal := (),
|
||||
publish := {},
|
||||
publishLocal := {},
|
||||
resolvers += Resolver.typesafeIvyRepo("releases")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,36 @@
|
|||
package example.test
|
||||
|
||||
import minitest._
|
||||
import scala.sys.process._
|
||||
import scala.sys.process.*
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
import sbt.io.IO
|
||||
import verify.BasicTestSuite
|
||||
|
||||
object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
|
||||
object ExtendedRunnerTest extends BasicTestSuite:
|
||||
// 1.3.0, 1.3.0-M4
|
||||
private[test] val versionRegEx = "\\d(\\.\\d+){2}(-\\w+)?"
|
||||
|
||||
lazy val isWindows: Boolean = sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("windows")
|
||||
lazy val isMac: Boolean = sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("mac")
|
||||
lazy val sbtScript =
|
||||
if (isWindows) new File("target/universal/stage/bin/sbt.bat")
|
||||
else new File("target/universal/stage/bin/sbt")
|
||||
if (isWindows) new File("launcher-package/target/universal/stage/bin/sbt.bat")
|
||||
else new File("launcher-package/target/universal/stage/bin/sbt")
|
||||
|
||||
def sbtProcess(args: String*) = sbtProcessWithOpts(args: _*)("", "")
|
||||
def sbtProcess(args: String*) = sbtProcessWithOpts(args*)("", "")
|
||||
def sbtProcessWithOpts(args: String*)(javaOpts: String, sbtOpts: String) =
|
||||
sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, new File("citest"),
|
||||
Process(
|
||||
Seq(sbtScript.getAbsolutePath) ++ args,
|
||||
new File("launcher-package/citest"),
|
||||
"JAVA_OPTS" -> javaOpts,
|
||||
"SBT_OPTS" -> sbtOpts)
|
||||
"SBT_OPTS" -> sbtOpts
|
||||
)
|
||||
def sbtProcessInDir(dir: File)(args: String*) =
|
||||
sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, dir,
|
||||
Process(
|
||||
Seq(sbtScript.getAbsolutePath) ++ args,
|
||||
dir,
|
||||
"JAVA_OPTS" -> "",
|
||||
"SBT_OPTS" -> "")
|
||||
"SBT_OPTS" -> ""
|
||||
)
|
||||
|
||||
test("sbt runs") {
|
||||
assert(sbtScript.exists)
|
||||
|
|
@ -52,12 +58,12 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
|
|||
val out3 = sbtProcess("-V").!!.trim
|
||||
testVersion(out3.linesIterator.toList)
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
test("sbt -V in empty directory") {
|
||||
IO.withTemporaryDirectory { tmp =>
|
||||
val out = sbtProcessInDir(tmp)("-V").!!.trim
|
||||
val expectedVersion = "^"+versionRegEx+"$"
|
||||
val expectedVersion = "^" + versionRegEx + "$"
|
||||
val targetDir = new File(tmp, "target")
|
||||
assert(!targetDir.exists, "expected target directory to not exist, but existed")
|
||||
}
|
||||
|
|
@ -71,12 +77,18 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
|
|||
assert(out.matches(expectedVersion))
|
||||
()
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
test("sbt --sbt-jar should run") {
|
||||
val out = sbtProcess("compile", "-v", "--sbt-jar", "../target/universal/stage/bin/sbt-launch.jar").!!.linesIterator.toList
|
||||
assert(out.contains[String]("../target/universal/stage/bin/sbt-launch.jar") ||
|
||||
out.contains[String]("\"../target/universal/stage/bin/sbt-launch.jar\"")
|
||||
val out = sbtProcess(
|
||||
"compile",
|
||||
"-v",
|
||||
"--sbt-jar",
|
||||
"../target/universal/stage/bin/sbt-launch.jar"
|
||||
).!!.linesIterator.toList
|
||||
assert(
|
||||
out.contains[String]("../target/universal/stage/bin/sbt-launch.jar") ||
|
||||
out.contains[String]("\"../target/universal/stage/bin/sbt-launch.jar\"")
|
||||
)
|
||||
()
|
||||
}
|
||||
|
|
@ -105,7 +117,7 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
|
|||
test("sbt --script-version in empty directory") {
|
||||
IO.withTemporaryDirectory { tmp =>
|
||||
val out = sbtProcessInDir(tmp)("--script-version").!!.trim
|
||||
val expectedVersion = "^"+versionRegEx+"$"
|
||||
val expectedVersion = "^" + versionRegEx + "$"
|
||||
assert(out.matches(expectedVersion))
|
||||
}
|
||||
()
|
||||
|
|
@ -126,4 +138,4 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions {
|
|||
}
|
||||
()
|
||||
}
|
||||
}
|
||||
end ExtendedRunnerTest
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package sbt.internal
|
||||
|
||||
import java.lang.{ ProcessBuilder => JProcessBuilder }
|
||||
|
||||
private[sbt] object InheritInput {
|
||||
def apply(p: JProcessBuilder): Boolean = (redirectInput, inherit) match {
|
||||
case (Some(m), Some(f)) =>
|
||||
m.invoke(p, f); true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private[this] val pbClass = Class.forName("java.lang.ProcessBuilder")
|
||||
private[this] val redirectClass = pbClass.getClasses find (_.getSimpleName == "Redirect")
|
||||
|
||||
private[this] val redirectInput = redirectClass map (pbClass.getMethod("redirectInput", _))
|
||||
private[this] val inherit = redirectClass map (_ getField "INHERIT" get null)
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package example.test
|
||||
|
||||
import com.eed3si9n.expecty.Expecty
|
||||
|
||||
trait PowerAssertions {
|
||||
lazy val assert: Expecty = new Expecty()
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
package sbt.internal
|
||||
|
||||
import java.lang.{ Process => JProcess, ProcessBuilder => JProcessBuilder }
|
||||
import java.io.{ Closeable, File, IOException }
|
||||
import java.io.{ BufferedReader, InputStream, InputStreamReader, OutputStream, PipedInputStream, PipedOutputStream }
|
||||
import java.net.URL
|
||||
|
||||
trait ProcessExtra {
|
||||
import Process._
|
||||
implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder)
|
||||
implicit def fileToProcess(file: File): FilePartialBuilder = apply(file)
|
||||
implicit def urlToProcess(url: URL): URLPartialBuilder = apply(url)
|
||||
|
||||
implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => SourcePartialBuilder): Seq[SourcePartialBuilder] = applySeq(builders)
|
||||
|
||||
implicit def stringToProcess(command: String): ProcessBuilder = apply(command)
|
||||
implicit def stringSeqToProcess(command: Seq[String]): ProcessBuilder = apply(command)
|
||||
}
|
||||
|
||||
/** Methods for constructing simple commands that can then be combined. */
|
||||
object Process extends ProcessExtra {
|
||||
def apply(command: String): ProcessBuilder = apply(command, None)
|
||||
|
||||
def apply(command: Seq[String]): ProcessBuilder = apply(command.toArray, None)
|
||||
|
||||
def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command :: arguments.toList, None)
|
||||
/** create ProcessBuilder with working dir set to File and extra environment variables */
|
||||
def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder =
|
||||
apply(command, Some(cwd), extraEnv: _*)
|
||||
/** create ProcessBuilder with working dir set to File and extra environment variables */
|
||||
def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder =
|
||||
apply(command, Some(cwd), extraEnv: _*)
|
||||
/** create ProcessBuilder with working dir optionally set to File and extra environment variables */
|
||||
def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = {
|
||||
apply(command.split("""\s+"""), cwd, extraEnv: _*)
|
||||
// not smart to use this on windows, because CommandParser uses \ to escape ".
|
||||
/*CommandParser.parse(command) match {
|
||||
case Left(errorMsg) => error(errorMsg)
|
||||
case Right((cmd, args)) => apply(cmd :: args, cwd, extraEnv : _*)
|
||||
}*/
|
||||
}
|
||||
/** create ProcessBuilder with working dir optionally set to File and extra environment variables */
|
||||
def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = {
|
||||
val jpb = new JProcessBuilder(command.toArray: _*)
|
||||
cwd.foreach(jpb directory _)
|
||||
extraEnv.foreach { case (k, v) => jpb.environment.put(k, v) }
|
||||
apply(jpb)
|
||||
}
|
||||
def apply(builder: JProcessBuilder): ProcessBuilder = new SimpleProcessBuilder(builder)
|
||||
def apply(file: File): FilePartialBuilder = new FileBuilder(file)
|
||||
def apply(url: URL): URLPartialBuilder = new URLBuilder(url)
|
||||
|
||||
def applySeq[T](builders: Seq[T])(implicit convert: T => SourcePartialBuilder): Seq[SourcePartialBuilder] = builders.map(convert)
|
||||
|
||||
def apply(value: Boolean): ProcessBuilder = apply(value.toString, if (value) 0 else 1)
|
||||
def apply(name: String, exitValue: => Int): ProcessBuilder = new DummyProcessBuilder(name, exitValue)
|
||||
|
||||
def cat(file: SourcePartialBuilder, files: SourcePartialBuilder*): ProcessBuilder = cat(file :: files.toList)
|
||||
def cat(files: Seq[SourcePartialBuilder]): ProcessBuilder =
|
||||
{
|
||||
require(files.nonEmpty)
|
||||
files.map(_.cat).reduceLeft(_ #&& _)
|
||||
}
|
||||
}
|
||||
|
||||
trait SourcePartialBuilder extends NotNull {
|
||||
/** Writes the output stream of this process to the given file. */
|
||||
def #>(f: File): ProcessBuilder = toFile(f, false)
|
||||
/** Appends the output stream of this process to the given file. */
|
||||
def #>>(f: File): ProcessBuilder = toFile(f, true)
|
||||
/**
|
||||
* Writes the output stream of this process to the given OutputStream. The
|
||||
* argument is call-by-name, so the stream is recreated, written, and closed each
|
||||
* time this process is executed.
|
||||
*/
|
||||
def #>(out: => OutputStream): ProcessBuilder = #>(new OutputStreamBuilder(out))
|
||||
def #>(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(toSource, b, false, ExitCodes.firstIfNonzero)
|
||||
private def toFile(f: File, append: Boolean) = #>(new FileOutput(f, append))
|
||||
def cat = toSource
|
||||
protected def toSource: ProcessBuilder
|
||||
}
|
||||
trait SinkPartialBuilder extends NotNull {
|
||||
/** Reads the given file into the input stream of this process. */
|
||||
def #<(f: File): ProcessBuilder = #<(new FileInput(f))
|
||||
/** Reads the given URL into the input stream of this process. */
|
||||
def #<(f: URL): ProcessBuilder = #<(new URLInput(f))
|
||||
/**
|
||||
* Reads the given InputStream into the input stream of this process. The
|
||||
* argument is call-by-name, so the stream is recreated, read, and closed each
|
||||
* time this process is executed.
|
||||
*/
|
||||
def #<(in: => InputStream): ProcessBuilder = #<(new InputStreamBuilder(in))
|
||||
def #<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, toSink, false, ExitCodes.firstIfNonzero)
|
||||
protected def toSink: ProcessBuilder
|
||||
}
|
||||
|
||||
trait URLPartialBuilder extends SourcePartialBuilder
|
||||
trait FilePartialBuilder extends SinkPartialBuilder with SourcePartialBuilder {
|
||||
def #<<(f: File): ProcessBuilder
|
||||
def #<<(u: URL): ProcessBuilder
|
||||
def #<<(i: => InputStream): ProcessBuilder
|
||||
def #<<(p: ProcessBuilder): ProcessBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a process that is running or has finished running.
|
||||
* It may be a compound process with several underlying native processes (such as 'a #&& b`).
|
||||
*/
|
||||
trait Process extends NotNull {
|
||||
/** Blocks until this process exits and returns the exit code.*/
|
||||
def exitValue(): Int
|
||||
/** Destroys this process. */
|
||||
def destroy(): Unit
|
||||
}
|
||||
/** Represents a runnable process. */
|
||||
trait ProcessBuilder extends SourcePartialBuilder with SinkPartialBuilder {
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is
|
||||
* sent to the console. If the exit code is non-zero, an exception is thrown.
|
||||
*/
|
||||
def !! : String
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is
|
||||
* sent to the provided ProcessLogger. If the exit code is non-zero, an exception is thrown.
|
||||
*/
|
||||
def !!(log: ProcessLogger): String
|
||||
/**
|
||||
* Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
|
||||
* but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value,
|
||||
* the Stream will provide all lines up to termination and then throw an exception.
|
||||
*/
|
||||
def lines: Stream[String]
|
||||
/**
|
||||
* Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
|
||||
* but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value,
|
||||
* the Stream will provide all lines up to termination but will not throw an exception.
|
||||
*/
|
||||
def lines(log: ProcessLogger): Stream[String]
|
||||
/**
|
||||
* Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
|
||||
* but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value,
|
||||
* the Stream will provide all lines up to termination but will not throw an exception.
|
||||
*/
|
||||
def lines_! : Stream[String]
|
||||
/**
|
||||
* Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
|
||||
* but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value,
|
||||
* the Stream will provide all lines up to termination but will not throw an exception.
|
||||
*/
|
||||
def lines_!(log: ProcessLogger): Stream[String]
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the console.
|
||||
*/
|
||||
def ! : Int
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the given ProcessLogger.
|
||||
*/
|
||||
def !(log: ProcessLogger): Int
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the console. The newly started process reads from standard input of the current process.
|
||||
*/
|
||||
def !< : Int
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the given ProcessLogger. The newly started process reads from standard input of the current process.
|
||||
*/
|
||||
def !<(log: ProcessLogger): Int
|
||||
/** Starts the process represented by this builder. Standard output and error are sent to the console.*/
|
||||
def run(): Process
|
||||
/** Starts the process represented by this builder. Standard output and error are sent to the given ProcessLogger.*/
|
||||
def run(log: ProcessLogger): Process
|
||||
/** Starts the process represented by this builder. I/O is handled by the given ProcessIO instance.*/
|
||||
def run(io: ProcessIO): Process
|
||||
/**
|
||||
* Starts the process represented by this builder. Standard output and error are sent to the console.
|
||||
* The newly started process reads from standard input of the current process if `connectInput` is true.
|
||||
*/
|
||||
def run(connectInput: Boolean): Process
|
||||
/**
|
||||
* Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
|
||||
* sent to the given ProcessLogger.
|
||||
* The newly started process reads from standard input of the current process if `connectInput` is true.
|
||||
*/
|
||||
def run(log: ProcessLogger, connectInput: Boolean): Process
|
||||
|
||||
def runBuffered(log: ProcessLogger, connectInput: Boolean): Process
|
||||
|
||||
/** Constructs a command that runs this command first and then `other` if this command succeeds.*/
|
||||
def #&&(other: ProcessBuilder): ProcessBuilder
|
||||
/** Constructs a command that runs this command first and then `other` if this command does not succeed.*/
|
||||
def #||(other: ProcessBuilder): ProcessBuilder
|
||||
/**
|
||||
* Constructs a command that will run this command and pipes the output to `other`.
|
||||
* `other` must be a simple command.
|
||||
* The exit code will be that of `other` regardless of whether this command succeeds.
|
||||
*/
|
||||
def #|(other: ProcessBuilder): ProcessBuilder
|
||||
/** Constructs a command that will run this command and then `other`. The exit code will be the exit code of `other`.*/
|
||||
def ###(other: ProcessBuilder): ProcessBuilder
|
||||
|
||||
def canPipeTo: Boolean
|
||||
}
|
||||
/** Each method will be called in a separate thread.*/
|
||||
final class ProcessIO(val writeInput: OutputStream => Unit, val processOutput: InputStream => Unit, val processError: InputStream => Unit, val inheritInput: JProcessBuilder => Boolean) extends NotNull {
|
||||
def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError, inheritInput)
|
||||
def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process, inheritInput)
|
||||
def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError, inheritInput)
|
||||
}
|
||||
trait ProcessLogger {
|
||||
def info(s: => String): Unit
|
||||
def error(s: => String): Unit
|
||||
def buffer[T](f: => T): T
|
||||
}
|
||||
|
|
@ -1,433 +0,0 @@
|
|||
package sbt.internal
|
||||
|
||||
import java.lang.{ Process => JProcess, ProcessBuilder => JProcessBuilder }
|
||||
import java.io.{ BufferedReader, Closeable, InputStream, InputStreamReader, IOException, OutputStream, PrintStream }
|
||||
import java.io.{ FilterInputStream, FilterOutputStream, PipedInputStream, PipedOutputStream }
|
||||
import java.io.{ File, FileInputStream, FileOutputStream }
|
||||
import java.net.URL
|
||||
|
||||
/** Runs provided code in a new Thread and returns the Thread instance. */
|
||||
private object Spawn {
|
||||
def apply(f: => Unit): Thread = apply(f, false)
|
||||
def apply(f: => Unit, daemon: Boolean): Thread =
|
||||
{
|
||||
val thread = new Thread() { override def run() = { f } }
|
||||
thread.setDaemon(daemon)
|
||||
thread.start()
|
||||
thread
|
||||
}
|
||||
}
|
||||
private object Future {
|
||||
def apply[T](f: => T): () => T =
|
||||
{
|
||||
val result = new SyncVar[Either[Throwable, T]]
|
||||
def run(): Unit =
|
||||
try { result.set(Right(f)) }
|
||||
catch { case e: Exception => result.set(Left(e)) }
|
||||
Spawn(run)
|
||||
() =>
|
||||
result.get match {
|
||||
case Right(value) => value
|
||||
case Left(exception) => throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object BasicIO {
|
||||
def apply(buffer: StringBuffer, log: Option[ProcessLogger], withIn: Boolean) = new ProcessIO(input(withIn), processFully(buffer), getErr(log), inheritInput(withIn))
|
||||
def apply(log: ProcessLogger, withIn: Boolean) = new ProcessIO(input(withIn), processInfoFully(log), processErrFully(log), inheritInput(withIn))
|
||||
|
||||
def getErr(log: Option[ProcessLogger]) = log match { case Some(lg) => processErrFully(lg); case None => toStdErr }
|
||||
|
||||
private def processErrFully(log: ProcessLogger) = processFully(s => log.error(s))
|
||||
private def processInfoFully(log: ProcessLogger) = processFully(s => log.info(s))
|
||||
|
||||
def closeOut = (_: OutputStream).close()
|
||||
final val BufferSize = 8192
|
||||
final val Newline = System.getProperty("line.separator")
|
||||
|
||||
def close(c: java.io.Closeable) = try { c.close() } catch { case _: java.io.IOException => () }
|
||||
def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer))
|
||||
def processFully(processLine: String => Unit): InputStream => Unit =
|
||||
in =>
|
||||
{
|
||||
val reader = new BufferedReader(new InputStreamReader(in))
|
||||
processLinesFully(processLine)(reader.readLine)
|
||||
reader.close()
|
||||
}
|
||||
def processLinesFully(processLine: String => Unit)(readLine: () => String): Unit = {
|
||||
def readFully(): Unit = {
|
||||
val line = readLine()
|
||||
if (line != null) {
|
||||
processLine(line)
|
||||
readFully()
|
||||
}
|
||||
}
|
||||
readFully()
|
||||
}
|
||||
def connectToIn(o: OutputStream): Unit = transferFully(Uncloseable protect System.in, o)
|
||||
def input(connect: Boolean): OutputStream => Unit = if (connect) connectToIn else closeOut
|
||||
def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput), inheritInput(connectInput))
|
||||
def standard(in: OutputStream => Unit, inheritIn: JProcessBuilder => Boolean): ProcessIO = new ProcessIO(in, toStdOut, toStdErr, inheritIn)
|
||||
|
||||
def toStdErr = (in: InputStream) => transferFully(in, System.err)
|
||||
def toStdOut = (in: InputStream) => transferFully(in, System.out)
|
||||
|
||||
def transferFully(in: InputStream, out: OutputStream): Unit =
|
||||
try { transferFullyImpl(in, out) }
|
||||
catch { case _: InterruptedException => () }
|
||||
|
||||
private[this] def appendLine(buffer: Appendable): String => Unit =
|
||||
line =>
|
||||
{
|
||||
buffer.append(line)
|
||||
buffer.append(Newline)
|
||||
}
|
||||
|
||||
private[this] def transferFullyImpl(in: InputStream, out: OutputStream): Unit = {
|
||||
val continueCount = 1 //if(in.isInstanceOf[PipedInputStream]) 1 else 0
|
||||
val buffer = new Array[Byte](BufferSize)
|
||||
def read(): Unit = {
|
||||
val byteCount = in.read(buffer)
|
||||
if (byteCount >= continueCount) {
|
||||
out.write(buffer, 0, byteCount)
|
||||
out.flush()
|
||||
read
|
||||
}
|
||||
}
|
||||
read
|
||||
in.close()
|
||||
}
|
||||
|
||||
def inheritInput(connect: Boolean) = { p: JProcessBuilder => if (connect) InheritInput(p) else false }
|
||||
}
|
||||
private[sbt] object ExitCodes {
|
||||
def ignoreFirst: (Int, Int) => Int = (a, b) => b
|
||||
def firstIfNonzero: (Int, Int) => Int = (a, b) => if (a != 0) a else b
|
||||
}
|
||||
|
||||
private[sbt] abstract class AbstractProcessBuilder extends ProcessBuilder with SinkPartialBuilder with SourcePartialBuilder {
|
||||
def #&&(other: ProcessBuilder): ProcessBuilder = new AndProcessBuilder(this, other)
|
||||
def #||(other: ProcessBuilder): ProcessBuilder = new OrProcessBuilder(this, other)
|
||||
def #|(other: ProcessBuilder): ProcessBuilder =
|
||||
{
|
||||
require(other.canPipeTo, "Piping to multiple processes is not supported.")
|
||||
new PipedProcessBuilder(this, other, false, exitCode = ExitCodes.ignoreFirst)
|
||||
}
|
||||
def ###(other: ProcessBuilder): ProcessBuilder = new SequenceProcessBuilder(this, other)
|
||||
|
||||
protected def toSource = this
|
||||
protected def toSink = this
|
||||
|
||||
def run(): Process = run(false)
|
||||
def run(connectInput: Boolean): Process = run(BasicIO.standard(connectInput))
|
||||
def run(log: ProcessLogger): Process = run(log, false)
|
||||
def run(log: ProcessLogger, connectInput: Boolean): Process = run(BasicIO(log, connectInput))
|
||||
|
||||
private[this] def getString(log: Option[ProcessLogger], withIn: Boolean): String =
|
||||
{
|
||||
val buffer = new StringBuffer
|
||||
val code = this ! BasicIO(buffer, log, withIn)
|
||||
if (code == 0) buffer.toString else sys.error("Nonzero exit value: " + code)
|
||||
}
|
||||
def !! = getString(None, false)
|
||||
def !!(log: ProcessLogger) = getString(Some(log), false)
|
||||
def !!< = getString(None, true)
|
||||
def !!<(log: ProcessLogger) = getString(Some(log), true)
|
||||
|
||||
def lines: Stream[String] = lines(false, true, None)
|
||||
def lines(log: ProcessLogger): Stream[String] = lines(false, true, Some(log))
|
||||
def lines_! : Stream[String] = lines(false, false, None)
|
||||
def lines_!(log: ProcessLogger): Stream[String] = lines(false, false, Some(log))
|
||||
|
||||
private[this] def lines(withInput: Boolean, nonZeroException: Boolean, log: Option[ProcessLogger]): Stream[String] =
|
||||
{
|
||||
val streamed = Streamed[String](nonZeroException)
|
||||
val process = run(new ProcessIO(BasicIO.input(withInput), BasicIO.processFully(streamed.process), BasicIO.getErr(log), BasicIO.inheritInput(withInput)))
|
||||
Spawn { streamed.done(process.exitValue()) }
|
||||
streamed.stream()
|
||||
}
|
||||
|
||||
def ! = run(false).exitValue()
|
||||
def !< = run(true).exitValue()
|
||||
def !(log: ProcessLogger) = runBuffered(log, false).exitValue()
|
||||
def !<(log: ProcessLogger) = runBuffered(log, true).exitValue()
|
||||
def runBuffered(log: ProcessLogger, connectInput: Boolean) =
|
||||
log.buffer { run(log, connectInput) }
|
||||
def !(io: ProcessIO) = run(io).exitValue()
|
||||
|
||||
def canPipeTo = false
|
||||
}
|
||||
|
||||
private[sbt] class URLBuilder(url: URL) extends URLPartialBuilder with SourcePartialBuilder {
|
||||
protected def toSource = new URLInput(url)
|
||||
}
|
||||
private[sbt] class FileBuilder(base: File) extends FilePartialBuilder with SinkPartialBuilder with SourcePartialBuilder {
|
||||
protected def toSource = new FileInput(base)
|
||||
protected def toSink = new FileOutput(base, false)
|
||||
def #<<(f: File): ProcessBuilder = #<<(new FileInput(f))
|
||||
def #<<(u: URL): ProcessBuilder = #<<(new URLInput(u))
|
||||
def #<<(s: => InputStream): ProcessBuilder = #<<(new InputStreamBuilder(s))
|
||||
def #<<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, new FileOutput(base, true), false, ExitCodes.firstIfNonzero)
|
||||
}
|
||||
|
||||
private abstract class BasicBuilder extends AbstractProcessBuilder {
|
||||
protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.")
|
||||
final def run(io: ProcessIO): Process =
|
||||
{
|
||||
val p = createProcess(io)
|
||||
p.start()
|
||||
p
|
||||
}
|
||||
protected[this] def createProcess(io: ProcessIO): BasicProcess
|
||||
}
|
||||
private abstract class BasicProcess extends Process {
|
||||
def start(): Unit
|
||||
}
|
||||
|
||||
private abstract class CompoundProcess extends BasicProcess {
|
||||
def destroy(): Unit = destroyer()
|
||||
def exitValue() = getExitValue().getOrElse(sys.error("No exit code: process destroyed."))
|
||||
|
||||
def start() = getExitValue
|
||||
|
||||
protected lazy val (getExitValue, destroyer) =
|
||||
{
|
||||
val code = new SyncVar[Option[Int]]()
|
||||
code.set(None)
|
||||
val thread = Spawn(code.set(runAndExitValue()))
|
||||
|
||||
(
|
||||
Future { thread.join(); code.get },
|
||||
() => thread.interrupt()
|
||||
)
|
||||
}
|
||||
|
||||
/** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/
|
||||
protected[this] def runAndExitValue(): Option[Int]
|
||||
|
||||
protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] =
|
||||
{
|
||||
try { Some(action) }
|
||||
catch { case _: InterruptedException => destroyImpl; None }
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class SequentialProcessBuilder(a: ProcessBuilder, b: ProcessBuilder, operatorString: String) extends BasicBuilder {
|
||||
checkNotThis(a)
|
||||
checkNotThis(b)
|
||||
override def toString = " ( " + a + " " + operatorString + " " + b + " ) "
|
||||
}
|
||||
private class PipedProcessBuilder(first: ProcessBuilder, second: ProcessBuilder, toError: Boolean, exitCode: (Int, Int) => Int) extends SequentialProcessBuilder(first, second, if (toError) "#|!" else "#|") {
|
||||
override def createProcess(io: ProcessIO) = new PipedProcesses(first, second, io, toError, exitCode)
|
||||
}
|
||||
private class AndProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "#&&") {
|
||||
override def createProcess(io: ProcessIO) = new AndProcess(first, second, io)
|
||||
}
|
||||
private class OrProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "#||") {
|
||||
override def createProcess(io: ProcessIO) = new OrProcess(first, second, io)
|
||||
}
|
||||
private class SequenceProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "###") {
|
||||
override def createProcess(io: ProcessIO) = new ProcessSequence(first, second, io)
|
||||
}
|
||||
|
||||
private class SequentialProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO, evaluateSecondProcess: Int => Boolean) extends CompoundProcess {
|
||||
protected[this] override def runAndExitValue() =
|
||||
{
|
||||
val first = a.run(io)
|
||||
runInterruptible(first.exitValue)(first.destroy()) flatMap
|
||||
{ codeA =>
|
||||
if (evaluateSecondProcess(codeA)) {
|
||||
val second = b.run(io)
|
||||
runInterruptible(second.exitValue)(second.destroy())
|
||||
} else
|
||||
Some(codeA)
|
||||
}
|
||||
}
|
||||
}
|
||||
private class AndProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, _ == 0)
|
||||
private class OrProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, _ != 0)
|
||||
private class ProcessSequence(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, ignore => true)
|
||||
|
||||
private class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean, exitCode: (Int, Int) => Int) extends CompoundProcess {
|
||||
protected[this] override def runAndExitValue() =
|
||||
{
|
||||
val currentSource = new SyncVar[Option[InputStream]]
|
||||
val pipeOut = new PipedOutputStream
|
||||
val source = new PipeSource(currentSource, pipeOut, a.toString)
|
||||
source.start()
|
||||
|
||||
val pipeIn = new PipedInputStream(pipeOut)
|
||||
val currentSink = new SyncVar[Option[OutputStream]]
|
||||
val sink = new PipeSink(pipeIn, currentSink, b.toString)
|
||||
sink.start()
|
||||
|
||||
def handleOutOrError(fromOutput: InputStream) = currentSource.put(Some(fromOutput))
|
||||
|
||||
val firstIO =
|
||||
if (toError)
|
||||
defaultIO.withError(handleOutOrError)
|
||||
else
|
||||
defaultIO.withOutput(handleOutOrError)
|
||||
val secondIO = defaultIO.withInput(toInput => currentSink.put(Some(toInput)))
|
||||
|
||||
val second = b.run(secondIO)
|
||||
val first = a.run(firstIO)
|
||||
try {
|
||||
runInterruptible {
|
||||
val firstResult = first.exitValue
|
||||
currentSource.put(None)
|
||||
currentSink.put(None)
|
||||
val secondResult = second.exitValue
|
||||
exitCode(firstResult, secondResult)
|
||||
} {
|
||||
first.destroy()
|
||||
second.destroy()
|
||||
}
|
||||
} finally {
|
||||
BasicIO.close(pipeIn)
|
||||
BasicIO.close(pipeOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
private class PipeSource(currentSource: SyncVar[Option[InputStream]], pipe: PipedOutputStream, label: => String) extends Thread {
|
||||
final override def run(): Unit = {
|
||||
currentSource.get match {
|
||||
case Some(source) =>
|
||||
try { BasicIO.transferFully(source, pipe) }
|
||||
catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() }
|
||||
finally {
|
||||
BasicIO.close(source)
|
||||
currentSource.unset()
|
||||
}
|
||||
run()
|
||||
case None =>
|
||||
currentSource.unset()
|
||||
BasicIO.close(pipe)
|
||||
}
|
||||
}
|
||||
}
|
||||
private class PipeSink(pipe: PipedInputStream, currentSink: SyncVar[Option[OutputStream]], label: => String) extends Thread {
|
||||
final override def run(): Unit = {
|
||||
currentSink.get match {
|
||||
case Some(sink) =>
|
||||
try { BasicIO.transferFully(pipe, sink) }
|
||||
catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() }
|
||||
finally {
|
||||
BasicIO.close(sink)
|
||||
currentSink.unset()
|
||||
}
|
||||
run()
|
||||
case None =>
|
||||
currentSink.unset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] class DummyProcessBuilder(override val toString: String, exitValue: => Int) extends AbstractProcessBuilder {
|
||||
override def run(io: ProcessIO): Process = new DummyProcess(exitValue)
|
||||
override def canPipeTo = true
|
||||
}
|
||||
/**
|
||||
* A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O.
|
||||
* The implementation of `exitValue` waits until these threads die before returning.
|
||||
*/
|
||||
private class DummyProcess(action: => Int) extends Process {
|
||||
private[this] val exitCode = Future(action)
|
||||
override def exitValue() = exitCode()
|
||||
override def destroy(): Unit = ()
|
||||
}
|
||||
/** Represents a simple command without any redirection or combination. */
|
||||
private[sbt] class SimpleProcessBuilder(p: JProcessBuilder) extends AbstractProcessBuilder {
|
||||
override def run(io: ProcessIO): Process =
|
||||
{
|
||||
import io._
|
||||
val inherited = inheritInput(p)
|
||||
val process = p.start()
|
||||
|
||||
// spawn threads that process the output and error streams, and also write input if not inherited.
|
||||
if (!inherited)
|
||||
Spawn(writeInput(process.getOutputStream))
|
||||
val outThread = Spawn(processOutput(process.getInputStream))
|
||||
val errorThread =
|
||||
if (!p.redirectErrorStream)
|
||||
Spawn(processError(process.getErrorStream)) :: Nil
|
||||
else
|
||||
Nil
|
||||
new SimpleProcess(process, outThread :: errorThread)
|
||||
}
|
||||
override def toString = p.command.toString
|
||||
override def canPipeTo = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
|
||||
* output and error streams of the process.
|
||||
* The implementation of `exitValue` wait for the process to finish and then waits until the threads reading output and error streams die before
|
||||
* returning. Note that the thread that reads the input stream cannot be interrupted, see https://github.com/sbt/sbt/issues/327 and
|
||||
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4514257
|
||||
*/
|
||||
private class SimpleProcess(p: JProcess, outputThreads: List[Thread]) extends Process {
|
||||
override def exitValue() =
|
||||
{
|
||||
try {
|
||||
p.waitFor()
|
||||
} catch {
|
||||
case _: InterruptedException => p.destroy()
|
||||
}
|
||||
outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
|
||||
p.exitValue()
|
||||
}
|
||||
override def destroy() = p.destroy()
|
||||
}
|
||||
|
||||
private[sbt] class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)
|
||||
private[sbt] class URLInput(url: URL) extends InputStreamBuilder(url.openStream, url.toString)
|
||||
private[sbt] class FileInput(file: File) extends InputStreamBuilder(new FileInputStream(file), file.getAbsolutePath)
|
||||
|
||||
import Uncloseable.protect
|
||||
private[sbt] class OutputStreamBuilder(stream: => OutputStream, label: String) extends ThreadProcessBuilder(label, _.writeInput(protect(stream))) {
|
||||
def this(stream: => OutputStream) = this(stream, "<output stream>")
|
||||
}
|
||||
private[sbt] class InputStreamBuilder(stream: => InputStream, label: String) extends ThreadProcessBuilder(label, _.processOutput(protect(stream))) {
|
||||
def this(stream: => InputStream) = this(stream, "<input stream>")
|
||||
}
|
||||
|
||||
private[sbt] abstract class ThreadProcessBuilder(override val toString: String, runImpl: ProcessIO => Unit) extends AbstractProcessBuilder {
|
||||
override def run(io: ProcessIO): Process =
|
||||
{
|
||||
val success = new SyncVar[Boolean]
|
||||
success.put(false)
|
||||
new ThreadProcess(Spawn { runImpl(io); success.set(true) }, success)
|
||||
}
|
||||
}
|
||||
private[sbt] final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process {
|
||||
override def exitValue() =
|
||||
{
|
||||
thread.join()
|
||||
if (success.get) 0 else 1
|
||||
}
|
||||
override def destroy(): Unit = thread.interrupt()
|
||||
}
|
||||
|
||||
object Uncloseable {
|
||||
def apply(in: InputStream): InputStream = new FilterInputStream(in) { override def close(): Unit = () }
|
||||
def apply(out: OutputStream): OutputStream = new FilterOutputStream(out) { override def close(): Unit = () }
|
||||
def protect(in: InputStream): InputStream = if (in eq System.in) Uncloseable(in) else in
|
||||
def protect(out: OutputStream): OutputStream = if ((out eq System.out) || (out eq System.err)) Uncloseable(out) else out
|
||||
}
|
||||
private[sbt] object Streamed {
|
||||
def apply[T](nonzeroException: Boolean): Streamed[T] =
|
||||
{
|
||||
val q = new java.util.concurrent.LinkedBlockingQueue[Either[Int, T]]
|
||||
def next(): Stream[T] =
|
||||
q.take match {
|
||||
case Left(0) => Stream.empty
|
||||
case Left(code) => if (nonzeroException) sys.error("Nonzero exit code: " + code) else Stream.empty
|
||||
case Right(s) => Stream.cons(s, next)
|
||||
}
|
||||
new Streamed((s: T) => q.put(Right(s)), code => q.put(Left(code)), () => next())
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] final class Streamed[T](val process: T => Unit, val done: Int => Unit, val stream: () => Stream[T]) extends NotNull
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package example.test
|
||||
|
||||
/**
|
||||
* RunnerMemoryScriptTest is used to test the sbt shell script, for both macOS/Linux and Windows.
|
||||
*/
|
||||
object RunnerMemoryScriptTest extends verify.BasicTestSuite with ShellScriptUtil:
|
||||
|
||||
testOutput("sbt -mem 503")("-mem", "503", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
|
||||
testOutput("sbt with -mem 503, -Xmx in JAVA_OPTS", javaOpts = "-Xmx1024m")("-mem", "503", "-v"):
|
||||
(out: List[String]) =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xmx1024m"))
|
||||
|
||||
testOutput("sbt with -mem 503, -Xmx in SBT_OPTS", sbtOpts = "-Xmx1024m")("-mem", "503", "-v"):
|
||||
(out: List[String]) =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xmx1024m"))
|
||||
|
||||
testOutput("sbt with -mem 503, -Xss in JAVA_OPTS", javaOpts = "-Xss6m")("-mem", "503", "-v"):
|
||||
(out: List[String]) =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xss6m"))
|
||||
|
||||
testOutput("sbt with -mem 503, -Xss in SBT_OPTS", sbtOpts = "-Xss6m")("-mem", "503", "-v"):
|
||||
(out: List[String]) =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xss6m"))
|
||||
|
||||
testOutput(
|
||||
"sbt with -Xms2048M -Xmx2048M -Xss6M in JAVA_OPTS",
|
||||
javaOpts = "-Xms2048M -Xmx2048M -Xss6M"
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Xms2048M"))
|
||||
assert(out.contains[String]("-Xmx2048M"))
|
||||
assert(out.contains[String]("-Xss6M"))
|
||||
|
||||
testOutput(
|
||||
"sbt with -Xms2048M -Xmx2048M -Xss6M in SBT_OPTS",
|
||||
sbtOpts = "-Xms2048M -Xmx2048M -Xss6M"
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Xms2048M"))
|
||||
assert(out.contains[String]("-Xmx2048M"))
|
||||
assert(out.contains[String]("-Xss6M"))
|
||||
|
||||
testOutput(
|
||||
"sbt use .sbtopts file for memory options",
|
||||
sbtOptsFileContents = """-J-XX:MaxInlineLevel=20
|
||||
|-J-Xmx222m
|
||||
|-J-Xms111m
|
||||
|-J-Xss12m""".stripMargin
|
||||
)("compile", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
|
||||
testOutput(
|
||||
"sbt use JAVA_OPTS for memory options",
|
||||
javaOpts = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m"
|
||||
)("compile", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
|
||||
testOutput(
|
||||
"sbt use JAVA_TOOL_OPTIONS for memory options",
|
||||
javaToolOptions = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m"
|
||||
)("compile", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
|
||||
end RunnerMemoryScriptTest
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package example.test
|
||||
|
||||
/**
|
||||
* RunnerScriptTest is used to test the sbt shell script, for both macOS/Linux and Windows.
|
||||
*/
|
||||
object RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUtil:
|
||||
|
||||
testOutput("sbt -no-colors")("compile", "-no-colors", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
|
||||
testOutput("sbt --no-colors")("compile", "--no-colors", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
|
||||
testOutput("sbt --color=false")("compile", "--color=false", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.color=false"))
|
||||
|
||||
testOutput("sbt --no-colors in SBT_OPTS", sbtOpts = "--no-colors")("compile", "-v"):
|
||||
(out: List[String]) =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
|
||||
testOutput("sbt --no-server")("compile", "--no-server", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.server.autostart=false"))
|
||||
|
||||
testOutput("sbt --debug-inc")("compile", "--debug-inc", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dxsbt.inc.debug=true"))
|
||||
|
||||
testOutput("sbt --supershell=never")("compile", "--supershell=never", "-v"):
|
||||
(out: List[String]) => assert(out.contains[String]("-Dsbt.supershell=never"))
|
||||
|
||||
testOutput("sbt --timings")("compile", "--timings", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.task.timings=true"))
|
||||
|
||||
testOutput("sbt -D arguments")("-Dsbt.supershell=false", "compile", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.supershell=false"))
|
||||
|
||||
testOutput("sbt --sbt-version")("--sbt-version", "1.3.13", "-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.version=1.3.13"))
|
||||
|
||||
testOutput(
|
||||
name = "sbt with -Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 in SBT_OPTS",
|
||||
sbtOpts = "-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080",
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dhttp.proxyHost=proxy"))
|
||||
assert(out.contains[String]("-Dhttp.proxyPort=8080"))
|
||||
|
||||
testOutput(
|
||||
name = "sbt with -XX:ParallelGCThreads=16 -XX:PermSize=128M in SBT_OPTS",
|
||||
sbtOpts = "-XX:ParallelGCThreads=16 -XX:PermSize=128M",
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:ParallelGCThreads=16"))
|
||||
assert(out.contains[String]("-XX:PermSize=128M"))
|
||||
|
||||
testOutput(
|
||||
"sbt with -XX:+UseG1GC -XX:+PrintGC in JAVA_OPTS",
|
||||
javaOpts = "-XX:+UseG1GC -XX:+PrintGC"
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:+UseG1GC"))
|
||||
assert(out.contains[String]("-XX:+PrintGC"))
|
||||
assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC"))
|
||||
|
||||
testOutput(
|
||||
"sbt with -XX:-UseG1GC -XX:-PrintGC in SBT_OPTS",
|
||||
sbtOpts = "-XX:+UseG1GC -XX:+PrintGC"
|
||||
)(
|
||||
"-v"
|
||||
): (out: List[String]) =>
|
||||
assert(out.contains[String]("-XX:+UseG1GC"))
|
||||
assert(out.contains[String]("-XX:+PrintGC"))
|
||||
assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC"))
|
||||
|
||||
testOutput(
|
||||
"sbt with -debug in SBT_OPTS appears in sbt commands",
|
||||
javaOpts = "",
|
||||
sbtOpts = "-debug"
|
||||
)("compile", "-v"): (out: List[String]) =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
|
||||
// Debug argument must appear in the 'commands' section (after the sbt-launch.jar argument) to work
|
||||
val sbtLaunchMatcher = """^.+sbt-launch.jar["]{0,1}$""".r
|
||||
val locationOfSbtLaunchJarArg = out.zipWithIndex.collectFirst {
|
||||
case (arg, index) if sbtLaunchMatcher.findFirstIn(arg).nonEmpty => index
|
||||
}
|
||||
|
||||
assert(locationOfSbtLaunchJarArg.nonEmpty)
|
||||
|
||||
val argsAfterSbtLaunch = out.drop(locationOfSbtLaunchJarArg.get)
|
||||
assert(argsAfterSbtLaunch.contains("-debug"))
|
||||
()
|
||||
|
||||
testOutput("sbt --jvm-debug <port>")("--jvm-debug", "12345", "-v"): (out: List[String]) =>
|
||||
assert(
|
||||
out.contains[String]("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12345")
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/sbt/sbt/issues/8100
|
||||
// Debug agent output in SBT_OPTS should not break the launcher on Windows
|
||||
testOutput(
|
||||
"sbt with debug agent in SBT_OPTS",
|
||||
sbtOpts = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12346"
|
||||
)("-v"): (out: List[String]) =>
|
||||
assert(
|
||||
out.contains[String]("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12346")
|
||||
)
|
||||
|
||||
testOutput("sbt --no-share adds three system properties")("--no-share"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.global.base=project/.sbtboot"))
|
||||
assert(out.contains[String]("-Dsbt.boot.directory=project/.boot"))
|
||||
assert(out.contains[String]("-Dsbt.ivy.home=project/.ivy"))
|
||||
|
||||
testOutput("accept `--ivy` in `SBT_OPTS`", sbtOpts = "--ivy /ivy/dir")("-v"):
|
||||
(out: List[String]) =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
else assert(out.contains[String]("-Dsbt.ivy.home=/ivy/dir"))
|
||||
|
||||
testOutput("sbt --script-version should print sbtVersion")("--script-version"):
|
||||
(out: List[String]) =>
|
||||
val expectedVersion = "^" + ExtendedRunnerTest.versionRegEx + "$"
|
||||
assert(out.mkString(System.lineSeparator()).trim.matches(expectedVersion))
|
||||
()
|
||||
|
||||
testOutput("--sbt-cache")("--sbt-cache", "./cachePath"): (out: List[String]) =>
|
||||
assert(out.contains[String]("-Dsbt.global.localcache=./cachePath"))
|
||||
|
||||
end RunnerScriptTest
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
package example.test
|
||||
|
||||
import minitest._
|
||||
import sbt.io.IO
|
||||
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.nio.file.Files
|
||||
|
||||
object SbtScriptTest extends SimpleTestSuite with PowerAssertions {
|
||||
lazy val isWindows: Boolean =
|
||||
sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows")
|
||||
lazy val sbtScript =
|
||||
if (isWindows) new File("target/universal/stage/bin/sbt.bat")
|
||||
else new File("target/universal/stage/bin/sbt")
|
||||
|
||||
private val javaBinDir = new File("integration-test", "bin").getAbsolutePath
|
||||
|
||||
private def retry[A1](f: () => A1, maxAttempt: Int = 10): A1 =
|
||||
try {
|
||||
f()
|
||||
} catch {
|
||||
case _ if maxAttempt <= 1 =>
|
||||
Thread.sleep(100)
|
||||
retry(f, maxAttempt - 1)
|
||||
}
|
||||
|
||||
def makeTest(
|
||||
name: String,
|
||||
javaOpts: String = "",
|
||||
sbtOpts: String = "",
|
||||
sbtOptsFileContents: String = "",
|
||||
javaToolOptions: String = ""
|
||||
)(args: String*)(f: List[String] => Any) = {
|
||||
test(name) {
|
||||
val workingDirectory = Files.createTempDirectory("sbt-launcher-package-test").toFile
|
||||
retry(() => IO.copyDirectory(new File("citest"), workingDirectory))
|
||||
|
||||
try {
|
||||
val sbtOptsFile = new File(workingDirectory, ".sbtopts")
|
||||
sbtOptsFile.createNewFile()
|
||||
val writer = new PrintWriter(sbtOptsFile)
|
||||
try {
|
||||
writer.write(sbtOptsFileContents)
|
||||
} finally {
|
||||
writer.close()
|
||||
}
|
||||
val path = sys.env.getOrElse("PATH", sys.env("Path"))
|
||||
val out = sbt.internal.Process(
|
||||
Seq(sbtScript.getAbsolutePath) ++ args,
|
||||
workingDirectory,
|
||||
"JAVA_OPTS" -> javaOpts,
|
||||
"SBT_OPTS" -> sbtOpts,
|
||||
"JAVA_TOOL_OPTIONS" -> javaToolOptions,
|
||||
if (isWindows)
|
||||
"JAVACMD" -> new File(javaBinDir, "java").getAbsolutePath()
|
||||
else
|
||||
"PATH" -> (javaBinDir + File.pathSeparator + path)
|
||||
).!!.linesIterator.toList
|
||||
f(out)
|
||||
()
|
||||
} finally {
|
||||
IO.delete(workingDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeTest("sbt -no-colors")("compile", "-no-colors", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
}
|
||||
|
||||
makeTest("sbt --no-colors")("compile", "--no-colors", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
}
|
||||
|
||||
makeTest("sbt --color=false")("compile", "--color=false", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.color=false"))
|
||||
}
|
||||
|
||||
makeTest("sbt --no-colors in SBT_OPTS", sbtOpts = "--no-colors")("compile", "-v") {
|
||||
out: List[String] =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
assert(out.contains[String]("-Dsbt.log.noformat=true"))
|
||||
}
|
||||
|
||||
makeTest("sbt --no-server")("compile", "--no-server", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.server.autostart=false"))
|
||||
}
|
||||
|
||||
makeTest("sbt --debug-inc")("compile", "--debug-inc", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dxsbt.inc.debug=true"))
|
||||
}
|
||||
|
||||
makeTest("sbt --supershell=never")("compile", "--supershell=never", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.supershell=never"))
|
||||
}
|
||||
|
||||
makeTest("sbt --timings")("compile", "--timings", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.task.timings=true"))
|
||||
}
|
||||
|
||||
makeTest("sbt -D arguments")("-Dsbt.supershell=false", "compile", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.supershell=false"))
|
||||
}
|
||||
|
||||
makeTest("sbt --sbt-version")("--sbt-version", "1.3.13", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.version=1.3.13"))
|
||||
}
|
||||
|
||||
makeTest("sbt -mem 503")("-mem", "503", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -mem 503, -Xmx in JAVA_OPTS", javaOpts = "-Xmx1024m")("-mem", "503", "-v") {
|
||||
out: List[String] =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xmx1024m"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -mem 503, -Xmx in SBT_OPTS", sbtOpts = "-Xmx1024m")("-mem", "503", "-v") {
|
||||
out: List[String] =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xmx1024m"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -mem 503, -Xss in JAVA_OPTS", javaOpts = "-Xss6m")("-mem", "503", "-v") {
|
||||
out: List[String] =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xss6m"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -mem 503, -Xss in SBT_OPTS", sbtOpts = "-Xss6m")("-mem", "503", "-v") {
|
||||
out: List[String] =>
|
||||
assert(out.contains[String]("-Xmx503m"))
|
||||
assert(!out.contains[String]("-Xss6m"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt with -Xms2048M -Xmx2048M -Xss6M in JAVA_OPTS",
|
||||
javaOpts = "-Xms2048M -Xmx2048M -Xss6M"
|
||||
)("-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Xms2048M"))
|
||||
assert(out.contains[String]("-Xmx2048M"))
|
||||
assert(out.contains[String]("-Xss6M"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt with -Xms2048M -Xmx2048M -Xss6M in SBT_OPTS",
|
||||
sbtOpts = "-Xms2048M -Xmx2048M -Xss6M"
|
||||
)("-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Xms2048M"))
|
||||
assert(out.contains[String]("-Xmx2048M"))
|
||||
assert(out.contains[String]("-Xss6M"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
name = "sbt with -Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 in SBT_OPTS",
|
||||
sbtOpts = "-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080",
|
||||
)("-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dhttp.proxyHost=proxy"))
|
||||
assert(out.contains[String]("-Dhttp.proxyPort=8080"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
name = "sbt with -XX:ParallelGCThreads=16 -XX:PermSize=128M in SBT_OPTS",
|
||||
sbtOpts = "-XX:ParallelGCThreads=16 -XX:PermSize=128M",
|
||||
)("-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:ParallelGCThreads=16"))
|
||||
assert(out.contains[String]("-XX:PermSize=128M"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt with -XX:+UseG1GC -XX:+PrintGC in JAVA_OPTS",
|
||||
javaOpts = "-XX:+UseG1GC -XX:+PrintGC"
|
||||
)("-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:+UseG1GC"))
|
||||
assert(out.contains[String]("-XX:+PrintGC"))
|
||||
assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -XX:-UseG1GC -XX:-PrintGC in SBT_OPTS", sbtOpts = "-XX:+UseG1GC -XX:+PrintGC")(
|
||||
"-v"
|
||||
) { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:+UseG1GC"))
|
||||
assert(out.contains[String]("-XX:+PrintGC"))
|
||||
assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC"))
|
||||
}
|
||||
|
||||
makeTest("sbt with -debug in SBT_OPTS appears in sbt commands", javaOpts = "", sbtOpts = "-debug")("compile", "-v") {out: List[String] =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
|
||||
// Debug argument must appear in the 'commands' section (after the sbt-launch.jar argument) to work
|
||||
val sbtLaunchMatcher = """^.+sbt-launch.jar["]{0,1}$""".r
|
||||
val locationOfSbtLaunchJarArg = out.zipWithIndex.collectFirst {
|
||||
case (arg, index) if sbtLaunchMatcher.findFirstIn(arg).nonEmpty => index
|
||||
}
|
||||
|
||||
assert(locationOfSbtLaunchJarArg.nonEmpty)
|
||||
|
||||
val argsAfterSbtLaunch = out.drop(locationOfSbtLaunchJarArg.get)
|
||||
assert(argsAfterSbtLaunch.contains("-debug"))
|
||||
()
|
||||
}
|
||||
|
||||
makeTest("sbt --jvm-debug <port>")("--jvm-debug", "12345", "-v") { out: List[String] =>
|
||||
assert(
|
||||
out.contains[String]("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12345")
|
||||
)
|
||||
}
|
||||
|
||||
makeTest("sbt --no-share adds three system properties")("--no-share") { out: List[String] =>
|
||||
assert(out.contains[String]("-Dsbt.global.base=project/.sbtboot"))
|
||||
assert(out.contains[String]("-Dsbt.boot.directory=project/.boot"))
|
||||
assert(out.contains[String]("-Dsbt.ivy.home=project/.ivy"))
|
||||
}
|
||||
|
||||
makeTest("accept `--ivy` in `SBT_OPTS`", sbtOpts = "--ivy /ivy/dir")("-v") { out: List[String] =>
|
||||
if (isWindows) cancel("Test not supported on windows")
|
||||
assert(out.contains[String]("-Dsbt.ivy.home=/ivy/dir"))
|
||||
}
|
||||
|
||||
makeTest("sbt --script-version should print sbtVersion")("--script-version") { out: List[String] =>
|
||||
val expectedVersion = "^" + SbtRunnerTest.versionRegEx + "$"
|
||||
assert(out.mkString(System.lineSeparator()).trim.matches(expectedVersion))
|
||||
()
|
||||
}
|
||||
|
||||
makeTest("--sbt-cache")("--sbt-cache", "./cachePath") { out: List[String] =>
|
||||
assert(out.contains[String](s"-Dsbt.global.localcache=./cachePath"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt use .sbtopts file for memory options", sbtOptsFileContents =
|
||||
"""-J-XX:MaxInlineLevel=20
|
||||
|-J-Xmx222m
|
||||
|-J-Xms111m
|
||||
|-J-Xss12m""".stripMargin
|
||||
|
||||
)("compile", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt use JAVA_OPTS for memory options", javaOpts = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m"
|
||||
)("compile", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
}
|
||||
|
||||
makeTest(
|
||||
"sbt use JAVA_TOOL_OPTIONS for memory options", javaToolOptions = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m"
|
||||
)("compile", "-v") { out: List[String] =>
|
||||
assert(out.contains[String]("-XX:MaxInlineLevel=20"))
|
||||
assert(out.contains[String]("-Xmx222m"))
|
||||
assert(out.contains[String]("-Xms111m"))
|
||||
assert(out.contains[String]("-Xss12m"))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package example.test
|
||||
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.nio.file.Files
|
||||
import sbt.io.IO
|
||||
import verify.BasicTestSuite
|
||||
|
||||
trait ShellScriptUtil extends BasicTestSuite {
|
||||
val isWindows: Boolean =
|
||||
sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows")
|
||||
|
||||
private val javaBinDir = new File("launcher-package/integration-test/bin").getAbsolutePath
|
||||
|
||||
private def retry[A1](f: () => A1, maxAttempt: Int = 10): A1 =
|
||||
try {
|
||||
f()
|
||||
} catch {
|
||||
case _ if maxAttempt <= 1 =>
|
||||
Thread.sleep(100)
|
||||
retry(f, maxAttempt - 1)
|
||||
}
|
||||
|
||||
val sbtScript =
|
||||
if (isWindows) new File("launcher-package/target/universal/stage/bin/sbt.bat")
|
||||
else new File("launcher-package/target/universal/stage/bin/sbt")
|
||||
|
||||
/**
|
||||
* testOutput is a helper function to create a test for shell script.
|
||||
*/
|
||||
inline def testOutput(
|
||||
name: String,
|
||||
javaOpts: String = "",
|
||||
sbtOpts: String = "",
|
||||
sbtOptsFileContents: String = "",
|
||||
javaToolOptions: String = ""
|
||||
)(args: String*)(f: List[String] => Any) =
|
||||
test(name) {
|
||||
val workingDirectory = Files.createTempDirectory("sbt-launcher-package-test").toFile
|
||||
retry(() => IO.copyDirectory(new File("launcher-package/citest"), workingDirectory))
|
||||
|
||||
try
|
||||
val sbtOptsFile = new File(workingDirectory, ".sbtopts")
|
||||
sbtOptsFile.createNewFile()
|
||||
val writer = new PrintWriter(sbtOptsFile)
|
||||
try {
|
||||
writer.write(sbtOptsFileContents)
|
||||
} finally {
|
||||
writer.close()
|
||||
}
|
||||
val path = sys.env.getOrElse("PATH", sys.env("Path"))
|
||||
val out = scala.sys.process
|
||||
.Process(
|
||||
Seq(sbtScript.getAbsolutePath) ++ args,
|
||||
workingDirectory,
|
||||
"JAVA_OPTS" -> javaOpts,
|
||||
"SBT_OPTS" -> sbtOpts,
|
||||
"JAVA_TOOL_OPTIONS" -> javaToolOptions,
|
||||
if (isWindows)
|
||||
"JAVACMD" -> new File(javaBinDir, "java").getAbsolutePath()
|
||||
else
|
||||
"PATH" -> (javaBinDir + File.pathSeparator + path)
|
||||
)
|
||||
.!!
|
||||
.linesIterator
|
||||
.toList
|
||||
f(out)
|
||||
()
|
||||
finally IO.delete(workingDirectory)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package sbt.internal
|
||||
|
||||
// minimal copy of scala.concurrent.SyncVar since that version deprecated put and unset
|
||||
private[sbt] final class SyncVar[A] {
|
||||
private[this] var isDefined: Boolean = false
|
||||
private[this] var value: Option[A] = None
|
||||
|
||||
/** Waits until a value is set and then gets it. Does not clear the value */
|
||||
def get: A = synchronized {
|
||||
while (!isDefined) wait()
|
||||
value.get
|
||||
}
|
||||
|
||||
/** Waits until a value is set, gets it, and finally clears the value. */
|
||||
def take(): A = synchronized {
|
||||
try get finally unset()
|
||||
}
|
||||
|
||||
/** Sets the value, whether or not it is currently defined. */
|
||||
def set(x: A): Unit = synchronized {
|
||||
isDefined = true
|
||||
value = Some(x)
|
||||
notifyAll()
|
||||
}
|
||||
|
||||
/** Sets the value, first waiting until it is undefined if it is currently defined. */
|
||||
def put(x: A): Unit = synchronized {
|
||||
while (isDefined) wait()
|
||||
set(x)
|
||||
}
|
||||
|
||||
/** Clears the value, whether or not it is current defined. */
|
||||
def unset(): Unit = synchronized {
|
||||
isDefined = false
|
||||
value = None
|
||||
notifyAll()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
import sbt._
|
||||
import Keys._
|
||||
import com.jsuereth.sbtpgp.SbtPgp
|
||||
import com.typesafe.sbt.packager.universal.{ UniversalPlugin, UniversalDeployPlugin }
|
||||
import com.typesafe.sbt.packager.debian.{ DebianPlugin, DebianDeployPlugin }
|
||||
import com.typesafe.sbt.packager.rpm.{ RpmPlugin, RpmDeployPlugin }
|
||||
import com.jsuereth.sbtpgp.gpgExtension
|
||||
|
||||
object PackageSignerPlugin extends sbt.AutoPlugin {
|
||||
override def trigger = allRequirements
|
||||
override def requires = SbtPgp && UniversalDeployPlugin && DebianDeployPlugin && RpmDeployPlugin
|
||||
|
||||
import com.jsuereth.sbtpgp.PgpKeys._
|
||||
import UniversalPlugin.autoImport._
|
||||
import DebianPlugin.autoImport._
|
||||
import RpmPlugin.autoImport._
|
||||
|
||||
override def projectSettings: Seq[Setting[_]] =
|
||||
inConfig(Universal)(packageSignerSettings) ++
|
||||
inConfig(Debian)(packageSignerSettings) ++
|
||||
inConfig(Rpm)(packageSignerSettings)
|
||||
|
||||
def subExtension(art: Artifact, ext: String): Artifact =
|
||||
art.copy(extension = ext)
|
||||
|
||||
def packageSignerSettings: Seq[Setting[_]] = Seq(
|
||||
signedArtifacts := {
|
||||
val artifacts = packagedArtifacts.value
|
||||
val r = pgpSigner.value
|
||||
val skipZ = (skip in pgpSigner).value
|
||||
val s = streams.value
|
||||
if (!skipZ) {
|
||||
artifacts flatMap { case (art, f) =>
|
||||
Seq(art -> f,
|
||||
subExtension(art, art.extension + gpgExtension) ->
|
||||
r.sign(f, file(f.getAbsolutePath + gpgExtension), s))
|
||||
}
|
||||
}
|
||||
else artifacts
|
||||
},
|
||||
publishSignedConfiguration := Classpaths.publishConfig(
|
||||
signedArtifacts.value,
|
||||
None,
|
||||
resolverName = Classpaths.getPublishTo(publishTo.value).name,
|
||||
checksums = (checksums in publish).value,
|
||||
logging = ivyLoggingLevel.value,
|
||||
overwrite = isSnapshot.value),
|
||||
publishLocalSignedConfiguration := Classpaths.publishConfig(
|
||||
signedArtifacts.value,
|
||||
None,
|
||||
resolverName = "local",
|
||||
checksums = (checksums in publish).value,
|
||||
logging = ivyLoggingLevel.value,
|
||||
overwrite = isSnapshot.value),
|
||||
publishSigned := Classpaths.publishTask(publishSignedConfiguration, deliver).value,
|
||||
publishLocalSigned := Classpaths.publishTask(publishLocalSignedConfiguration, deliver).value
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
sbt.version=0.13.18
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.7.6")
|
||||
libraryDependencies += "net.databinder" %% "dispatch-http" % "0.8.10"
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-export-repo" % "0.1.1")
|
||||
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
|
||||
|
|
@ -6,6 +6,7 @@ object Dependencies {
|
|||
// WARNING: Please Scala update versions in PluginCross.scala too
|
||||
val scala212 = "2.12.21"
|
||||
val scala213 = "2.13.18"
|
||||
val scala3 = "3.7.4"
|
||||
val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up")
|
||||
val baseScalaVersion = scala212
|
||||
def nightlyVersion: Option[String] =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
import sbt.*
|
||||
import Keys.*
|
||||
import sbt.internal.librarymanagement.IvyActions
|
||||
import com.jsuereth.sbtpgp.SbtPgp
|
||||
import com.typesafe.sbt.packager.universal.{ UniversalPlugin, UniversalDeployPlugin }
|
||||
import com.typesafe.sbt.packager.debian.{ DebianPlugin, DebianDeployPlugin }
|
||||
import com.typesafe.sbt.packager.rpm.{ RpmPlugin, RpmDeployPlugin }
|
||||
import com.jsuereth.sbtpgp.gpgExtension
|
||||
|
||||
object PackageSignerPlugin extends sbt.AutoPlugin {
|
||||
override def trigger = allRequirements
|
||||
override def requires = SbtPgp && UniversalDeployPlugin && DebianDeployPlugin && RpmDeployPlugin
|
||||
|
||||
import com.jsuereth.sbtpgp.PgpKeys.*
|
||||
import UniversalPlugin.autoImport.*
|
||||
import DebianPlugin.autoImport.*
|
||||
import RpmPlugin.autoImport.*
|
||||
|
||||
override def projectSettings: Seq[Setting[?]] =
|
||||
inConfig(Universal)(packageSignerSettings) ++
|
||||
inConfig(Debian)(packageSignerSettings) ++
|
||||
inConfig(Rpm)(packageSignerSettings)
|
||||
|
||||
def subExtension(art: Artifact, ext: String): Artifact =
|
||||
art.withExtension(ext)
|
||||
|
||||
def packageSignerSettings: Seq[Setting[?]] = Seq(
|
||||
signedArtifacts := {
|
||||
val artifacts = packagedArtifacts.value
|
||||
val r = pgpSigner.value
|
||||
val skipZ = (pgpSigner / skip).value
|
||||
val s = streams.value
|
||||
if (!skipZ) {
|
||||
artifacts flatMap {
|
||||
case (art, f) =>
|
||||
Seq(
|
||||
art -> f,
|
||||
subExtension(art, art.extension + gpgExtension) ->
|
||||
r.sign(f, file(f.getAbsolutePath + gpgExtension), s)
|
||||
)
|
||||
}
|
||||
} else artifacts
|
||||
},
|
||||
publishSignedConfiguration := Classpaths.publishConfig(
|
||||
publishMavenStyle = publishMavenStyle.value,
|
||||
deliverIvyPattern =
|
||||
(Compile / packageBin / artifactPath).value.getParent + "/[artifact]-[revision](-[classifier]).[ext]",
|
||||
status = if (isSnapshot.value) "integration" else "release",
|
||||
configurations = Vector.empty,
|
||||
artifacts = signedArtifacts.value.toVector,
|
||||
checksums = (publish / checksums).value.toVector,
|
||||
resolverName = Classpaths.getPublishTo(publishTo.value).name,
|
||||
logging = ivyLoggingLevel.value,
|
||||
overwrite = isSnapshot.value
|
||||
),
|
||||
publishLocalSignedConfiguration := Classpaths.publishConfig(
|
||||
publishMavenStyle = publishMavenStyle.value,
|
||||
deliverIvyPattern =
|
||||
(Compile / packageBin / artifactPath).value.getParent + "/[artifact]-[revision](-[classifier]).[ext]",
|
||||
status = if (isSnapshot.value) "integration" else "release",
|
||||
configurations = Vector.empty,
|
||||
artifacts = signedArtifacts.value.toVector,
|
||||
checksums = (publish / checksums).value.toVector,
|
||||
resolverName = "local",
|
||||
logging = ivyLoggingLevel.value,
|
||||
overwrite = isSnapshot.value
|
||||
),
|
||||
publishSigned := Def.taskDyn {
|
||||
val config = publishSignedConfiguration.value
|
||||
val s = streams.value
|
||||
Def.task {
|
||||
IvyActions.publish(ivyModule.value, config, s.log)
|
||||
}
|
||||
}.value,
|
||||
publishLocalSigned := Def.taskDyn {
|
||||
val config = publishLocalSignedConfiguration.value
|
||||
val s = streams.value
|
||||
Def.task {
|
||||
IvyActions.publish(ivyModule.value, config, s.log)
|
||||
}
|
||||
}.value
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -12,3 +12,5 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1")
|
|||
addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1")
|
||||
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.1")
|
||||
addDependencyTreePlugin
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.3")
|
||||
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4")
|
||||
|
|
|
|||
Loading…
Reference in New Issue