From 57caee6152a8aba14b6746b6b8c1cba132c42cb8 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Wed, 10 Jan 2018 12:48:07 +0900 Subject: [PATCH 01/21] update mimaPreviousArtifacts. add sbt 1.1.0 --- build.sbt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index dcedffd62..c9d250357 100644 --- a/build.sbt +++ b/build.sbt @@ -76,9 +76,11 @@ def testedBaseSettings: Seq[Setting[_]] = baseSettings ++ testDependencies val mimaSettings = Def settings ( - mimaPreviousArtifacts := (0 to 4).map { v => - organization.value % moduleName.value % s"1.0.$v" cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) - }.toSet + mimaPreviousArtifacts := { + ((0 to 4).map(v => s"1.0.$v") ++ (0 to 0).map(v => s"1.1.$v")).map{ v => + organization.value % moduleName.value % v cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) + }.toSet + } ) lazy val sbtRoot: Project = (project in file(".")) @@ -318,6 +320,7 @@ lazy val commandProj = (project in file("main-command")) contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats, mimaSettings, mimaBinaryIssueFilters ++= Vector( + exclude[DirectMissingMethodProblem]("sbt.BasicCommands.rebootOptionParser"), // Changed the signature of Server method. nacho cheese. exclude[DirectMissingMethodProblem]("sbt.internal.server.Server.*"), // Added method to ServerInstance. This is also internal. @@ -379,6 +382,9 @@ lazy val mainSettingsProj = (project in file("main-settings")) name := "Main Settings", resourceGenerators in Compile += generateToolboxClasspath.taskValue, mimaSettings, + mimaBinaryIssueFilters ++= Seq( + exclude[DirectMissingMethodProblem]("sbt.Scope.display012StyleMasked"), + ), ) .configure( addSbtIO, @@ -449,6 +455,9 @@ lazy val sbtProj = (project in file("sbt")) lazy val sbtIgnoredProblems = { Vector( + exclude[MissingClassProblem]("buildinfo.BuildInfo"), + exclude[MissingClassProblem]("buildinfo.BuildInfo$"), + // Added more items to Import trait. exclude[ReversedMissingMethodProblem]("sbt.Import.sbt$Import$_setter_$WatchSource_="), exclude[ReversedMissingMethodProblem]("sbt.Import.WatchSource"), From e58b30329706b5119ef9cdba716eb005f0b2b454 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 03:27:11 -0500 Subject: [PATCH 02/21] 1.1.1-SNAPSHOT --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index c9d250357..a4e810d3b 100644 --- a/build.sbt +++ b/build.sbt @@ -9,7 +9,7 @@ def buildLevelSettings: Seq[Setting[_]] = inThisBuild( Seq( organization := "org.scala-sbt", - version := "1.1.0-SNAPSHOT", + version := "1.1.1-SNAPSHOT", description := "sbt is an interactive build tool", bintrayOrganization := Some("sbt"), bintrayRepository := { From 0885233281151877ae5613ab87e095dbff1575e2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 10 Jan 2018 10:44:45 +0000 Subject: [PATCH 03/21] Deprecates Extracted#append for appendWithSession. .. and appendWithoutSession. --- main/src/main/scala/sbt/Extracted.scala | 23 +++++++++++++++++++++-- notes/1.1.1/fix-Extracted.append.md | 11 +++++++++++ notes/{1.0.2 => 1.1.1}/sample.md | 0 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 notes/1.1.1/fix-Extracted.append.md rename notes/{1.0.2 => 1.1.1}/sample.md (100%) diff --git a/main/src/main/scala/sbt/Extracted.scala b/main/src/main/scala/sbt/Extracted.scala index 80243ada1..f507fa2c7 100644 --- a/main/src/main/scala/sbt/Extracted.scala +++ b/main/src/main/scala/sbt/Extracted.scala @@ -120,10 +120,29 @@ final case class Extracted(structure: BuildStructure, structure.data.get(scope, key) getOrElse sys.error( display.show(ScopedKey(scope, key)) + " is undefined.") - def append(settings: Seq[Setting[_]], state: State): State = { + @deprecated("This discards session settings. Migrate to appendWithSession or appendWithoutSession.", "1.2.0") + def append(settings: Seq[Setting[_]], state: State): State = + appendWithoutSession(settings, state) + + /** Appends the given settings to all the build state settings, including session settings. */ + def appendWithSession(settings: Seq[Setting[_]], state: State): State = + appendImpl(settings, state, session.mergeSettings) + + /** + * Appends the given settings to the original build state settings, discarding any settings + * appended to the session in the process. + */ + def appendWithoutSession(settings: Seq[Setting[_]], state: State): State = + appendImpl(settings, state, session.original) + + private[this] def appendImpl( + settings: Seq[Setting[_]], + state: State, + sessionSettings: Seq[Setting[_]], + ): State = { val appendSettings = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, settings) - val newStructure = Load.reapply(session.original ++ appendSettings, structure) + val newStructure = Load.reapply(sessionSettings ++ appendSettings, structure) Project.setProject(session, newStructure, state) } } diff --git a/notes/1.1.1/fix-Extracted.append.md b/notes/1.1.1/fix-Extracted.append.md new file mode 100644 index 000000000..56e450391 --- /dev/null +++ b/notes/1.1.1/fix-Extracted.append.md @@ -0,0 +1,11 @@ +[@dwijnand]: https://github.com/dwijnand + +[#3865]: https://github.com/sbt/sbt/pull/3865 + +### Fixes with compatibility implications + +### Improvements + +- Deprecates `Extracted#append` in favour of `appendWithSession` or `appendWithoutSession`. [#3865][] by [@dwijnand][] + +### Bug fixes diff --git a/notes/1.0.2/sample.md b/notes/1.1.1/sample.md similarity index 100% rename from notes/1.0.2/sample.md rename to notes/1.1.1/sample.md From ca712acf41c2c5e37e6ce7ac8756ac5a6c6f3cb6 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 13 Jan 2018 03:37:29 -0500 Subject: [PATCH 04/21] Fix tab completion running `sbt console` Fixes #3841 This fixes console task that internally uses JLine. When `console` is started from batch mode, the tab is printed as is. This is because JLine is not initialized yet. Calling `usingTerminal` initializes and restores the terminal afterwards. --- main/src/main/scala/sbt/Defaults.scala | 13 +++++++------ .../scala/sbt/internal/ConsoleProject.scala | 19 ++++++++++++------- notes/1.1.1/console_jline.md | 13 +++++++++++++ notes/{1.0.2 => 1.1.1}/sample.md | 0 4 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 notes/1.1.1/console_jline.md rename notes/{1.0.2 => 1.1.1}/sample.md (100%) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 4fd8d1585..721e08914 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1366,12 +1366,13 @@ object Defaults extends BuildCommon { (compilers in task).value.scalac match { case ac: AnalyzingCompiler => ac.onArgs(exported(s, "scala")) } - (new Console(compiler))(cpFiles, - (scalacOptions in task).value, - loader, - (initialCommands in task).value, - (cleanupCommands in task).value)()(s.log).get - println() + val sc = (scalacOptions in task).value + val ic = (initialCommands in task).value + val cc = (cleanupCommands in task).value + JLine.usingTerminal { _ => + (new Console(compiler))(cpFiles, sc, loader, ic, cc)()(s.log).get + println() + } } private[this] def exported(w: PrintWriter, command: String): Seq[String] => Unit = diff --git a/main/src/main/scala/sbt/internal/ConsoleProject.scala b/main/src/main/scala/sbt/internal/ConsoleProject.scala index bc3756460..c2e2edef2 100644 --- a/main/src/main/scala/sbt/internal/ConsoleProject.scala +++ b/main/src/main/scala/sbt/internal/ConsoleProject.scala @@ -9,6 +9,7 @@ package sbt package internal import sbt.util.Logger +import sbt.internal.util.JLine import sbt.internal.inc.{ ScalaInstance, ZincUtil } import xsbti.compile.ClasspathOptionsUtil @@ -42,13 +43,17 @@ object ConsoleProject { val imports = BuildUtil.getImports(unit.unit) ++ BuildUtil.importAll(bindings.map(_._1)) val importString = imports.mkString("", ";\n", ";\n\n") val initCommands = importString + extra - // TODO - Hook up dsl classpath correctly... - (new Console(compiler))( - unit.classpath, - options, - initCommands, - cleanupCommands - )(Some(unit.loader), bindings) + + JLine.usingTerminal { _ => + // TODO - Hook up dsl classpath correctly... + (new Console(compiler))( + unit.classpath, + options, + initCommands, + cleanupCommands + )(Some(unit.loader), bindings) + } + () } /** Conveniences for consoleProject that shouldn't normally be used for builds. */ diff --git a/notes/1.1.1/console_jline.md b/notes/1.1.1/console_jline.md new file mode 100644 index 000000000..fd851ce16 --- /dev/null +++ b/notes/1.1.1/console_jline.md @@ -0,0 +1,13 @@ + [@eed3si9n]: https://github.com/eed3si9n + + [3841]: https://github.com/sbt/sbt/issues/3841 + [3876]: https://github.com/sbt/sbt/pull/3876 + +### Fixes with compatibility implications + +### Improvements + + +### Bug fixes + +- Fixes tab completion in `console` while running in batch mode as `sbt console`. [#3841][3841]/[#3876][3876] by [@eed3si9n][@eed3si9n] diff --git a/notes/1.0.2/sample.md b/notes/1.1.1/sample.md similarity index 100% rename from notes/1.0.2/sample.md rename to notes/1.1.1/sample.md From c310ade9f8d389bcd82ae2159340170ade79ad77 Mon Sep 17 00:00:00 2001 From: Antonio Cunei Date: Tue, 30 Jan 2018 23:59:37 +0100 Subject: [PATCH 05/21] Allow the full tests/fork-parallel to pass on less than four cores The test will be meaningless on less than four cores, but the scripted test will pass. On four or more cores, the test wil test testForkedParallel as expected. Closes #3545 --- sbt/src/sbt-test/tests/fork-parallel/build.sbt | 2 ++ sbt/src/sbt-test/tests/fork-parallel/test | 17 +++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/sbt/src/sbt-test/tests/fork-parallel/build.sbt b/sbt/src/sbt-test/tests/fork-parallel/build.sbt index e28535d49..0647f55e9 100644 --- a/sbt/src/sbt-test/tests/fork-parallel/build.sbt +++ b/sbt/src/sbt-test/tests/fork-parallel/build.sbt @@ -13,6 +13,8 @@ lazy val root = (project in file(".")). val log = streams.value.log if( nbProc < 4 ) { log.warn("With fewer than 4 processors this test is meaningless") + // mimic behavior expected by scripted + if (!testForkedParallel.value) sys.error("Exiting with error (note: test not performed)") } else { // we've got at least 4 processors, we'll check the upper end but also 3 and 4 as the upper might not // be reached if the system is under heavy load. diff --git a/sbt/src/sbt-test/tests/fork-parallel/test b/sbt/src/sbt-test/tests/fork-parallel/test index 662328f42..70e542b98 100644 --- a/sbt/src/sbt-test/tests/fork-parallel/test +++ b/sbt/src/sbt-test/tests/fork-parallel/test @@ -1,18 +1,7 @@ -# The tests/fork-parallel test will currently always -# report success when run on less than four cores, -# rather than failing in one of the two cases as expected. -# TODO: Adjust this scripted test so that it works as -# intended on less than four cores as well. - -# To debug, it is possible to limit the number of cores -# reported to sbt, and run the test, by using: -# taskset 0x00000003 sbt 'scripted tests/fork-parallel' -# See: https://github.com/sbt/sbt/issues/3545 - -# This bit won't currently work when using less than four cores. -# > test -# -> check +# Note: this test is meaningless on less than four cores +> test +-> check > clean > set testForkedParallel := true > test From 0aa133d2762aad2ea3a52d77dbbbac8bdc3dc0f6 Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Mon, 5 Feb 2018 23:11:42 -0800 Subject: [PATCH 06/21] Implement 'suppressServer' setting, for builds and plugins that prefer to be conservative about exposure to other processes. --- main-command/src/main/scala/sbt/BasicKeys.scala | 6 ++++++ main/src/main/scala/sbt/Defaults.scala | 1 + main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/Project.scala | 3 +++ main/src/main/scala/sbt/internal/CommandExchange.scala | 7 ++++++- 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 86dd9343f..a820515db 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -39,6 +39,12 @@ object BasicKeys { "The wire protocol for the server command.", 10000) + val suppressServer = + AttributeKey[Boolean]( + "suppressServer", + "Running the server will be suppressed if 'suppressServer is explicitly set to true.", + 10000) + // Unlike other BasicKeys, this is not used directly as a setting key, // and severLog / logLevel is used instead. private[sbt] val serverLogLevel = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 721e08914..35c9db19d 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -268,6 +268,7 @@ object Defaults extends BuildCommon { .getOrElse(GCUtil.defaultForceGarbageCollection), minForcegcInterval :== GCUtil.defaultMinForcegcInterval, interactionService :== CommandLineUIService, + suppressServer := false, serverHost := "127.0.0.1", serverPort := 5000 + (Hash .toHex(Hash(appConfiguration.value.baseDirectory.toString)) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index bfcb3139e..1d32e2bb5 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -131,6 +131,7 @@ object Keys { // Command keys val historyPath = SettingKey(BasicKeys.historyPath) val shellPrompt = SettingKey(BasicKeys.shellPrompt) + val suppressServer = SettingKey(BasicKeys.suppressServer) val serverPort = SettingKey(BasicKeys.serverPort) val serverHost = SettingKey(BasicKeys.serverHost) val serverAuthentication = SettingKey(BasicKeys.serverAuthentication) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index fd175f6c6..bd9fe8b0b 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -21,6 +21,7 @@ import Keys.{ sessionSettings, shellPrompt, templateResolverInfos, + suppressServer, serverHost, serverLog, serverPort, @@ -462,6 +463,7 @@ object Project extends ProjectExtra { val prompt = get(shellPrompt) val trs = (templateResolverInfos in Global get structure.data).toList.flatten val watched = get(watch) + val suppressSvr: Option[Boolean] = get(suppressServer) val host: Option[String] = get(serverHost) val port: Option[Int] = get(serverPort) val authentication: Option[Set[ServerAuthentication]] = get(serverAuthentication) @@ -474,6 +476,7 @@ object Project extends ProjectExtra { s.attributes .setCond(Watched.Configuration, watched) .put(historyPath.key, history) + .setCond(suppressServer.key, suppressSvr) .setCond(serverPort.key, port) .setCond(serverHost.key, host) .setCond(serverAuthentication.key, authentication) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index d7b52280d..eeadfad33 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -14,6 +14,7 @@ import java.util.concurrent.atomic._ import scala.collection.mutable.ListBuffer import scala.annotation.tailrec import BasicKeys.{ + suppressServer, serverHost, serverPort, serverAuthentication, @@ -87,7 +88,11 @@ private[sbt] final class CommandExchange { consoleChannel = Some(x) subscribe(x) } - if (autoStartServer) runServer(s) + val suppress = (s get suppressServer) match { + case Some(bool) => bool + case None => false + } + if (autoStartServer && !suppress) runServer(s) else s } From 9601668199cd52d6fa37aecae4cbda10920f89bf Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Mon, 5 Feb 2018 23:35:31 -0800 Subject: [PATCH 07/21] Add a note describing the suppressServer feature. --- notes/1.1.1/suppressServer.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 notes/1.1.1/suppressServer.md diff --git a/notes/1.1.1/suppressServer.md b/notes/1.1.1/suppressServer.md new file mode 100644 index 000000000..0e9fdce79 --- /dev/null +++ b/notes/1.1.1/suppressServer.md @@ -0,0 +1,23 @@ +### Improvements + +This pull request implements a Boolean setting called `suppressServer`, whose default value is `false'. + +If a build or plugin explicitly sets it to `true`, the sbt-1.x server will not start up +(exactly as if `sbt.server.autostart` were set to start). + +Users may manually override this setting by running the `startServer` command at the interactive prompt. + +### Motivation + +Projects often encounter private information, such as deployment credentials, private keys, etc. +For such projects, it may be preferable to reduce the potential attack surface than to enjoy the +interoperability offered by sbt's server. Projects that wish to make this tradeoff can set `suppressServer` +to `true` in their build. Security-sensitive plugins can define this setting as well, modifying the +default behavior in favor of security. + +(My own motivation is that I am working on a [plugin for developing Ethereum applications](https://github.com/swaldman/sbt-ethereum) +with scala and sbt. It must work with extremely sensitive private keys.) + +--- + +See also a [recent conversation on Stack Exchange](https://stackoverflow.com/questions/48591179/can-one-disable-the-sbt-1-x-server/48593906#48593906). From 2db5c77442b62eacfc339c881bbac6489e036ccb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 6 Feb 2018 16:02:45 +0000 Subject: [PATCH 08/21] Upgrade Scala versions that ^^ uses Refs #3907 --- main/src/main/scala/sbt/PluginCross.scala | 4 ++-- project/Dependencies.scala | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 0e9e1462d..9828b42a7 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -92,8 +92,8 @@ private[sbt] object PluginCross { def scalaVersionFromSbtBinaryVersion(sv: String): String = VersionNumber(sv) match { case VersionNumber(Seq(0, 12, _*), _, _) => "2.9.2" - case VersionNumber(Seq(0, 13, _*), _, _) => "2.10.6" - case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.3" + case VersionNumber(Seq(0, 13, _*), _, _) => "2.10.7" + case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.4" case _ => sys.error(s"Unsupported sbt binary version: $sv") } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f8d470ccd..31e6b3c15 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,11 +3,7 @@ import Keys._ import sbt.contraband.ContrabandPlugin.autoImport._ object Dependencies { - val scala282 = "2.8.2" - val scala292 = "2.9.2" - val scala293 = "2.9.3" - val scala210 = "2.10.7" - val scala211 = "2.11.12" + // WARNING: Please Scala update versions in PluginCross.scala too val scala212 = "2.12.4" val baseScalaVersion = scala212 From 96b94296693a31fd4db61444b264f53bf718ee38 Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Tue, 6 Feb 2018 11:49:46 -0800 Subject: [PATCH 09/21] Rework false-defaulting 'suppressServer' to true-defaulting 'autoStartServer'. --- .../src/main/scala/sbt/BasicKeys.scala | 6 ++-- main/src/main/scala/sbt/Defaults.scala | 2 +- main/src/main/scala/sbt/Keys.scala | 2 +- main/src/main/scala/sbt/Project.scala | 6 ++-- .../scala/sbt/internal/CommandExchange.scala | 10 +++--- notes/1.1.1/autoStartServer.md | 31 +++++++++++++++++++ notes/1.1.1/suppressServer.md | 23 -------------- 7 files changed, 44 insertions(+), 36 deletions(-) create mode 100644 notes/1.1.1/autoStartServer.md delete mode 100644 notes/1.1.1/suppressServer.md diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index a820515db..1570d392b 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -39,10 +39,10 @@ object BasicKeys { "The wire protocol for the server command.", 10000) - val suppressServer = + val autoStartServer = AttributeKey[Boolean]( - "suppressServer", - "Running the server will be suppressed if 'suppressServer is explicitly set to true.", + "autoStartServer", + "If true, the sbt server will startup automatically during interactive sessions.", 10000) // Unlike other BasicKeys, this is not used directly as a setting key, diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 35c9db19d..b9ab939ef 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -268,7 +268,7 @@ object Defaults extends BuildCommon { .getOrElse(GCUtil.defaultForceGarbageCollection), minForcegcInterval :== GCUtil.defaultMinForcegcInterval, interactionService :== CommandLineUIService, - suppressServer := false, + autoStartServer := true, serverHost := "127.0.0.1", serverPort := 5000 + (Hash .toHex(Hash(appConfiguration.value.baseDirectory.toString)) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 1d32e2bb5..8e26b54fb 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -131,7 +131,7 @@ object Keys { // Command keys val historyPath = SettingKey(BasicKeys.historyPath) val shellPrompt = SettingKey(BasicKeys.shellPrompt) - val suppressServer = SettingKey(BasicKeys.suppressServer) + val autoStartServer = SettingKey(BasicKeys.autoStartServer) val serverPort = SettingKey(BasicKeys.serverPort) val serverHost = SettingKey(BasicKeys.serverHost) val serverAuthentication = SettingKey(BasicKeys.serverAuthentication) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index bd9fe8b0b..f292ca0e1 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -21,7 +21,7 @@ import Keys.{ sessionSettings, shellPrompt, templateResolverInfos, - suppressServer, + autoStartServer, serverHost, serverLog, serverPort, @@ -463,7 +463,7 @@ object Project extends ProjectExtra { val prompt = get(shellPrompt) val trs = (templateResolverInfos in Global get structure.data).toList.flatten val watched = get(watch) - val suppressSvr: Option[Boolean] = get(suppressServer) + val startSvr: Option[Boolean] = get(autoStartServer) val host: Option[String] = get(serverHost) val port: Option[Int] = get(serverPort) val authentication: Option[Set[ServerAuthentication]] = get(serverAuthentication) @@ -476,7 +476,7 @@ object Project extends ProjectExtra { s.attributes .setCond(Watched.Configuration, watched) .put(historyPath.key, history) - .setCond(suppressServer.key, suppressSvr) + .setCond(autoStartServer.key, startSvr) .setCond(serverPort.key, port) .setCond(serverHost.key, host) .setCond(serverAuthentication.key, authentication) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index eeadfad33..0b68cae24 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -14,7 +14,7 @@ import java.util.concurrent.atomic._ import scala.collection.mutable.ListBuffer import scala.annotation.tailrec import BasicKeys.{ - suppressServer, + autoStartServer, serverHost, serverPort, serverAuthentication, @@ -44,7 +44,7 @@ import sbt.util.{ Level, Logger, LogExchange } * this exchange, which could serve command request from either of the channel. */ private[sbt] final class CommandExchange { - private val autoStartServer = sys.props.get("sbt.server.autostart") map { + private val autoStartServerSysProp = sys.props.get("sbt.server.autostart") map { _.toLowerCase == "true" } getOrElse true private val lock = new AnyRef {} @@ -88,11 +88,11 @@ private[sbt] final class CommandExchange { consoleChannel = Some(x) subscribe(x) } - val suppress = (s get suppressServer) match { + val autoStartServerAttr = (s get autoStartServer) match { case Some(bool) => bool - case None => false + case None => true } - if (autoStartServer && !suppress) runServer(s) + if (autoStartServerSysProp && autoStartServerAttr) runServer(s) else s } diff --git a/notes/1.1.1/autoStartServer.md b/notes/1.1.1/autoStartServer.md new file mode 100644 index 000000000..cc00c3fbc --- /dev/null +++ b/notes/1.1.1/autoStartServer.md @@ -0,0 +1,31 @@ +### Improvements + +This pull request implements a Boolean setting called `autoStartServer`, whose default value is `true'. + +If a build or plugin explicitly sets it to `false`, the sbt-1.x server will not start up +(exactly as if the system property `sbt.server.autostart` were set to `false`). + +Users who set `autoStartServer` to `false` may manually execute `startServer` at the interactive prompt, +if they wish to use the server during a shell session. + +### Motivation + +Projects often encounter private information, such as deployment credentials, private keys, etc. +For such projects, it may be preferable to reduce the potential attack surface than to enjoy the +interoperability offered by sbt's server. Projects that wish to make this tradeoff can set `autoStartServer` +to `false` in their build. Security-sensitive plugins can disable `autoStartServer` as well, modifying the +default behavior in favor of security. + +(My own motivation is that I am working on a [plugin for developing Ethereum applications](https://github.com/swaldman/sbt-ethereum) +with scala and sbt. It must work with extremely sensitive private keys.) + +--- + +See also a [recent conversation on Stack Exchange](https://stackoverflow.com/questions/48591179/can-one-disable-the-sbt-1-x-server/48593906#48593906). + +--- + +##### History + +2018-02-06 Modified from negative `suppressServer` to positive `autoStartServer` at the (sensible) request of @eed3si9n + diff --git a/notes/1.1.1/suppressServer.md b/notes/1.1.1/suppressServer.md deleted file mode 100644 index 0e9fdce79..000000000 --- a/notes/1.1.1/suppressServer.md +++ /dev/null @@ -1,23 +0,0 @@ -### Improvements - -This pull request implements a Boolean setting called `suppressServer`, whose default value is `false'. - -If a build or plugin explicitly sets it to `true`, the sbt-1.x server will not start up -(exactly as if `sbt.server.autostart` were set to start). - -Users may manually override this setting by running the `startServer` command at the interactive prompt. - -### Motivation - -Projects often encounter private information, such as deployment credentials, private keys, etc. -For such projects, it may be preferable to reduce the potential attack surface than to enjoy the -interoperability offered by sbt's server. Projects that wish to make this tradeoff can set `suppressServer` -to `true` in their build. Security-sensitive plugins can define this setting as well, modifying the -default behavior in favor of security. - -(My own motivation is that I am working on a [plugin for developing Ethereum applications](https://github.com/swaldman/sbt-ethereum) -with scala and sbt. It must work with extremely sensitive private keys.) - ---- - -See also a [recent conversation on Stack Exchange](https://stackoverflow.com/questions/48591179/can-one-disable-the-sbt-1-x-server/48593906#48593906). From a2347332ab9271f75c2cb8a4881922d2eecbeb44 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 30 Jan 2018 00:43:30 -0500 Subject: [PATCH 10/21] Use ipcsocket --- build.sbt | 5 +- .../internal/NGUnixDomainServerSocket.java | 178 ---------------- .../java/sbt/internal/NGUnixDomainSocket.java | 192 ------------------ .../internal/NGUnixDomainSocketLibrary.java | 142 ------------- .../sbt/internal/NGWin32NamedPipeLibrary.java | 90 -------- .../NGWin32NamedPipeServerSocket.java | 173 ---------------- .../sbt/internal/NGWin32NamedPipeSocket.java | 172 ---------------- .../ReferenceCountedFileDescriptor.java | 82 -------- .../scala/sbt/internal/server/Server.scala | 7 +- project/Dependencies.scala | 1 + 10 files changed, 9 insertions(+), 1033 deletions(-) delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java delete mode 100644 main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java diff --git a/build.sbt b/build.sbt index a4e810d3b..17d4b477e 100644 --- a/build.sbt +++ b/build.sbt @@ -297,7 +297,7 @@ lazy val protocolProj = (project in file("protocol")) .settings( testedBaseSettings, name := "Protocol", - libraryDependencies ++= Seq(sjsonNewScalaJson.value), + libraryDependencies ++= Seq(sjsonNewScalaJson.value, ipcSocket), managedSourceDirectories in Compile += baseDirectory.value / "src" / "main" / "contraband-scala", sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", @@ -329,6 +329,9 @@ lazy val commandProj = (project in file("main-command")) exclude[ReversedMissingMethodProblem]("sbt.internal.CommandChannel.*"), // Added an overload to reboot. The overload is private[sbt]. exclude[ReversedMissingMethodProblem]("sbt.StateOps.reboot"), + // Replace nailgun socket stuff + exclude[MissingClassProblem]("sbt.internal.NG*"), + exclude[MissingClassProblem]("sbt.internal.ReferenceCountedFileDescriptor"), ), unmanagedSources in (Compile, headerCreate) := { val old = (unmanagedSources in (Compile, headerCreate)).value diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java b/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java deleted file mode 100644 index 89d3bcf43..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainServerSocket.java +++ /dev/null @@ -1,178 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainServerSocket.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.concurrent.atomic.AtomicInteger; - -import com.sun.jna.LastErrorException; -import com.sun.jna.ptr.IntByReference; - -/** - * Implements a {@link ServerSocket} which binds to a local Unix domain socket - * and returns instances of {@link NGUnixDomainSocket} from - * {@link #accept()}. - */ -public class NGUnixDomainServerSocket extends ServerSocket { - private static final int DEFAULT_BACKLOG = 50; - - // We use an AtomicInteger to prevent a race in this situation which - // could happen if fd were just an int: - // - // Thread 1 -> NGUnixDomainServerSocket.accept() - // -> lock this - // -> check isBound and isClosed - // -> unlock this - // -> descheduled while still in method - // Thread 2 -> NGUnixDomainServerSocket.close() - // -> lock this - // -> check isClosed - // -> NGUnixDomainSocketLibrary.close(fd) - // -> now fd is invalid - // -> unlock this - // Thread 1 -> re-scheduled while still in method - // -> NGUnixDomainSocketLibrary.accept(fd, which is invalid and maybe re-used) - // - // By using an AtomicInteger, we'll set this to -1 after it's closed, which - // will cause the accept() call above to cleanly fail instead of possibly - // being called on an unrelated fd (which may or may not fail). - private final AtomicInteger fd; - - private final int backlog; - private boolean isBound; - private boolean isClosed; - - public static class NGUnixDomainServerSocketAddress extends SocketAddress { - private final String path; - - public NGUnixDomainServerSocketAddress(String path) { - this.path = path; - } - - public String getPath() { - return path; - } - } - - /** - * Constructs an unbound Unix domain server socket. - */ - public NGUnixDomainServerSocket() throws IOException { - this(DEFAULT_BACKLOG, null); - } - - /** - * Constructs an unbound Unix domain server socket with the specified listen backlog. - */ - public NGUnixDomainServerSocket(int backlog) throws IOException { - this(backlog, null); - } - - /** - * Constructs and binds a Unix domain server socket to the specified path. - */ - public NGUnixDomainServerSocket(String path) throws IOException { - this(DEFAULT_BACKLOG, path); - } - - /** - * Constructs and binds a Unix domain server socket to the specified path - * with the specified listen backlog. - */ - public NGUnixDomainServerSocket(int backlog, String path) throws IOException { - try { - fd = new AtomicInteger( - NGUnixDomainSocketLibrary.socket( - NGUnixDomainSocketLibrary.PF_LOCAL, - NGUnixDomainSocketLibrary.SOCK_STREAM, - 0)); - this.backlog = backlog; - if (path != null) { - bind(new NGUnixDomainServerSocketAddress(path)); - } - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public synchronized void bind(SocketAddress endpoint) throws IOException { - if (!(endpoint instanceof NGUnixDomainServerSocketAddress)) { - throw new IllegalArgumentException( - "endpoint must be an instance of NGUnixDomainServerSocketAddress"); - } - if (isBound) { - throw new IllegalStateException("Socket is already bound"); - } - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - NGUnixDomainServerSocketAddress unEndpoint = (NGUnixDomainServerSocketAddress) endpoint; - NGUnixDomainSocketLibrary.SockaddrUn address = - new NGUnixDomainSocketLibrary.SockaddrUn(unEndpoint.getPath()); - try { - int socketFd = fd.get(); - NGUnixDomainSocketLibrary.bind(socketFd, address, address.size()); - NGUnixDomainSocketLibrary.listen(socketFd, backlog); - isBound = true; - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public Socket accept() throws IOException { - // We explicitly do not make this method synchronized, since the - // call to NGUnixDomainSocketLibrary.accept() will block - // indefinitely, causing another thread's call to close() to deadlock. - synchronized (this) { - if (!isBound) { - throw new IllegalStateException("Socket is not bound"); - } - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - } - try { - NGUnixDomainSocketLibrary.SockaddrUn sockaddrUn = - new NGUnixDomainSocketLibrary.SockaddrUn(); - IntByReference addressLen = new IntByReference(); - addressLen.setValue(sockaddrUn.size()); - int clientFd = NGUnixDomainSocketLibrary.accept(fd.get(), sockaddrUn, addressLen); - return new NGUnixDomainSocket(clientFd); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - public synchronized void close() throws IOException { - if (isClosed) { - throw new IllegalStateException("Socket is already closed"); - } - try { - // Ensure any pending call to accept() fails. - NGUnixDomainSocketLibrary.close(fd.getAndSet(-1)); - isClosed = true; - } catch (LastErrorException e) { - throw new IOException(e); - } - } -} diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java b/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java deleted file mode 100644 index b70bef611..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainSocket.java +++ /dev/null @@ -1,192 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainSocket.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import java.nio.ByteBuffer; - -import java.net.Socket; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Implements a {@link Socket} backed by a native Unix domain socket. - * - * Instances of this class always return {@code null} for - * {@link Socket#getInetAddress()}, {@link Socket#getLocalAddress()}, - * {@link Socket#getLocalSocketAddress()}, {@link Socket#getRemoteSocketAddress()}. - */ -public class NGUnixDomainSocket extends Socket { - private final ReferenceCountedFileDescriptor fd; - private final InputStream is; - private final OutputStream os; - - public NGUnixDomainSocket(String path) throws IOException { - try { - AtomicInteger fd = new AtomicInteger( - NGUnixDomainSocketLibrary.socket( - NGUnixDomainSocketLibrary.PF_LOCAL, - NGUnixDomainSocketLibrary.SOCK_STREAM, - 0)); - NGUnixDomainSocketLibrary.SockaddrUn address = - new NGUnixDomainSocketLibrary.SockaddrUn(path); - int socketFd = fd.get(); - NGUnixDomainSocketLibrary.connect(socketFd, address, address.size()); - this.fd = new ReferenceCountedFileDescriptor(socketFd); - this.is = new NGUnixDomainSocketInputStream(); - this.os = new NGUnixDomainSocketOutputStream(); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - /** - * Creates a Unix domain socket backed by a native file descriptor. - */ - public NGUnixDomainSocket(int fd) { - this.fd = new ReferenceCountedFileDescriptor(fd); - this.is = new NGUnixDomainSocketInputStream(); - this.os = new NGUnixDomainSocketOutputStream(); - } - - public InputStream getInputStream() { - return is; - } - - public OutputStream getOutputStream() { - return os; - } - - public void shutdownInput() throws IOException { - doShutdown(NGUnixDomainSocketLibrary.SHUT_RD); - } - - public void shutdownOutput() throws IOException { - doShutdown(NGUnixDomainSocketLibrary.SHUT_WR); - } - - private void doShutdown(int how) throws IOException { - try { - int socketFd = fd.acquire(); - if (socketFd != -1) { - NGUnixDomainSocketLibrary.shutdown(socketFd, how); - } - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - - public void close() throws IOException { - super.close(); - try { - // This might not close the FD right away. In case we are about - // to read or write on another thread, it will delay the close - // until the read or write completes, to prevent the FD from - // being re-used for a different purpose and the other thread - // reading from a different FD. - fd.close(); - } catch (LastErrorException e) { - throw new IOException(e); - } - } - - private class NGUnixDomainSocketInputStream extends InputStream { - public int read() throws IOException { - ByteBuffer buf = ByteBuffer.allocate(1); - int result; - if (doRead(buf) == 0) { - result = -1; - } else { - // Make sure to & with 0xFF to avoid sign extension - result = 0xFF & buf.get(); - } - return result; - } - - public int read(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return 0; - } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); - int result = doRead(buf); - if (result == 0) { - result = -1; - } - return result; - } - - private int doRead(ByteBuffer buf) throws IOException { - try { - int fdToRead = fd.acquire(); - if (fdToRead == -1) { - return -1; - } - return NGUnixDomainSocketLibrary.read(fdToRead, buf, buf.remaining()); - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - } - - private class NGUnixDomainSocketOutputStream extends OutputStream { - - public void write(int b) throws IOException { - ByteBuffer buf = ByteBuffer.allocate(1); - buf.put(0, (byte) (0xFF & b)); - doWrite(buf); - } - - public void write(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return; - } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); - doWrite(buf); - } - - private void doWrite(ByteBuffer buf) throws IOException { - try { - int fdToWrite = fd.acquire(); - if (fdToWrite == -1) { - return; - } - int ret = NGUnixDomainSocketLibrary.write(fdToWrite, buf, buf.remaining()); - if (ret != buf.remaining()) { - // This shouldn't happen with standard blocking Unix domain sockets. - throw new IOException("Could not write " + buf.remaining() + " bytes as requested " + - "(wrote " + ret + " bytes instead)"); - } - } catch (LastErrorException e) { - throw new IOException(e); - } finally { - fd.release(); - } - } - } -} diff --git a/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java b/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java deleted file mode 100644 index 4d781b6b6..000000000 --- a/main-command/src/main/java/sbt/internal/NGUnixDomainSocketLibrary.java +++ /dev/null @@ -1,142 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGUnixDomainSocketLibrary.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Platform; -import com.sun.jna.Structure; -import com.sun.jna.Union; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; - -/** - * Utility class to bridge native Unix domain socket calls to Java using JNA. - */ -public class NGUnixDomainSocketLibrary { - public static final int PF_LOCAL = 1; - public static final int AF_LOCAL = 1; - public static final int SOCK_STREAM = 1; - - public static final int SHUT_RD = 0; - public static final int SHUT_WR = 1; - - // Utility class, do not instantiate. - private NGUnixDomainSocketLibrary() { } - - // BSD platforms write a length byte at the start of struct sockaddr_un. - private static final boolean HAS_SUN_LEN = - Platform.isMac() || Platform.isFreeBSD() || Platform.isNetBSD() || - Platform.isOpenBSD() || Platform.iskFreeBSD(); - - /** - * Bridges {@code struct sockaddr_un} to and from native code. - */ - public static class SockaddrUn extends Structure implements Structure.ByReference { - /** - * On BSD platforms, the {@code sun_len} and {@code sun_family} values in - * {@code struct sockaddr_un}. - */ - public static class SunLenAndFamily extends Structure { - public byte sunLen; - public byte sunFamily; - - protected List getFieldOrder() { - return Arrays.asList(new String[] { "sunLen", "sunFamily" }); - } - } - - /** - * On BSD platforms, {@code sunLenAndFamily} will be present. - * On other platforms, only {@code sunFamily} will be present. - */ - public static class SunFamily extends Union { - public SunLenAndFamily sunLenAndFamily; - public short sunFamily; - } - - public SunFamily sunFamily = new SunFamily(); - public byte[] sunPath = new byte[104]; - - /** - * Constructs an empty {@code struct sockaddr_un}. - */ - public SockaddrUn() { - if (HAS_SUN_LEN) { - sunFamily.sunLenAndFamily = new SunLenAndFamily(); - sunFamily.setType(SunLenAndFamily.class); - } else { - sunFamily.setType(Short.TYPE); - } - allocateMemory(); - } - - /** - * Constructs a {@code struct sockaddr_un} with a path whose bytes are encoded - * using the default encoding of the platform. - */ - public SockaddrUn(String path) throws IOException { - byte[] pathBytes = path.getBytes(); - if (pathBytes.length > sunPath.length - 1) { - throw new IOException("Cannot fit name [" + path + "] in maximum unix domain socket length"); - } - System.arraycopy(pathBytes, 0, sunPath, 0, pathBytes.length); - sunPath[pathBytes.length] = (byte) 0; - if (HAS_SUN_LEN) { - int len = fieldOffset("sunPath") + pathBytes.length; - sunFamily.sunLenAndFamily = new SunLenAndFamily(); - sunFamily.sunLenAndFamily.sunLen = (byte) len; - sunFamily.sunLenAndFamily.sunFamily = AF_LOCAL; - sunFamily.setType(SunLenAndFamily.class); - } else { - sunFamily.sunFamily = AF_LOCAL; - sunFamily.setType(Short.TYPE); - } - allocateMemory(); - } - - protected List getFieldOrder() { - return Arrays.asList(new String[] { "sunFamily", "sunPath" }); - } - } - - static { - Native.register(Platform.C_LIBRARY_NAME); - } - - public static native int socket(int domain, int type, int protocol) throws LastErrorException; - public static native int bind(int fd, SockaddrUn address, int addressLen) - throws LastErrorException; - public static native int listen(int fd, int backlog) throws LastErrorException; - public static native int accept(int fd, SockaddrUn address, IntByReference addressLen) - throws LastErrorException; - public static native int connect(int fd, SockaddrUn address, int addressLen) - throws LastErrorException; - public static native int read(int fd, ByteBuffer buffer, int count) - throws LastErrorException; - public static native int write(int fd, ByteBuffer buffer, int count) - throws LastErrorException; - public static native int close(int fd) throws LastErrorException; - public static native int shutdown(int fd, int how) throws LastErrorException; -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java deleted file mode 100644 index dd4d8f15a..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeLibrary.java - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import java.nio.ByteBuffer; - -import com.sun.jna.*; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.platform.win32.WinNT.*; -import com.sun.jna.platform.win32.WinBase.*; -import com.sun.jna.ptr.IntByReference; - -import com.sun.jna.win32.W32APIOptions; - -public interface NGWin32NamedPipeLibrary extends Library, WinNT { - int PIPE_ACCESS_DUPLEX = 3; - int PIPE_UNLIMITED_INSTANCES = 255; - int FILE_FLAG_FIRST_PIPE_INSTANCE = 524288; - - NGWin32NamedPipeLibrary INSTANCE = - (NGWin32NamedPipeLibrary) Native.loadLibrary( - "kernel32", - NGWin32NamedPipeLibrary.class, - W32APIOptions.UNICODE_OPTIONS); - - HANDLE CreateNamedPipe( - String lpName, - int dwOpenMode, - int dwPipeMode, - int nMaxInstances, - int nOutBufferSize, - int nInBufferSize, - int nDefaultTimeOut, - SECURITY_ATTRIBUTES lpSecurityAttributes); - boolean ConnectNamedPipe( - HANDLE hNamedPipe, - Pointer lpOverlapped); - boolean DisconnectNamedPipe( - HANDLE hObject); - boolean ReadFile( - HANDLE hFile, - Memory lpBuffer, - int nNumberOfBytesToRead, - IntByReference lpNumberOfBytesRead, - Pointer lpOverlapped); - boolean WriteFile( - HANDLE hFile, - ByteBuffer lpBuffer, - int nNumberOfBytesToWrite, - IntByReference lpNumberOfBytesWritten, - Pointer lpOverlapped); - boolean CloseHandle( - HANDLE hObject); - boolean GetOverlappedResult( - HANDLE hFile, - Pointer lpOverlapped, - IntByReference lpNumberOfBytesTransferred, - boolean wait); - boolean CancelIoEx( - HANDLE hObject, - Pointer lpOverlapped); - HANDLE CreateEvent( - SECURITY_ATTRIBUTES lpEventAttributes, - boolean bManualReset, - boolean bInitialState, - String lpName); - int WaitForSingleObject( - HANDLE hHandle, - int dwMilliseconds - ); - - int GetLastError(); -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java deleted file mode 100644 index 137d9b5dc..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeServerSocket.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeServerSocket.java - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.platform.win32.WinBase; -import com.sun.jna.platform.win32.WinError; -import com.sun.jna.platform.win32.WinNT; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; - -public class NGWin32NamedPipeServerSocket extends ServerSocket { - private static final NGWin32NamedPipeLibrary API = NGWin32NamedPipeLibrary.INSTANCE; - private static final String WIN32_PIPE_PREFIX = "\\\\.\\pipe\\"; - private static final int BUFFER_SIZE = 65535; - private final LinkedBlockingQueue openHandles; - private final LinkedBlockingQueue connectedHandles; - private final NGWin32NamedPipeSocket.CloseCallback closeCallback; - private final String path; - private final int maxInstances; - private final HANDLE lockHandle; - - public NGWin32NamedPipeServerSocket(String path) throws IOException { - this(NGWin32NamedPipeLibrary.PIPE_UNLIMITED_INSTANCES, path); - } - - public NGWin32NamedPipeServerSocket(int maxInstances, String path) throws IOException { - this.openHandles = new LinkedBlockingQueue<>(); - this.connectedHandles = new LinkedBlockingQueue<>(); - this.closeCallback = handle -> { - if (connectedHandles.remove(handle)) { - closeConnectedPipe(handle, false); - } - if (openHandles.remove(handle)) { - closeOpenPipe(handle); - } - }; - this.maxInstances = maxInstances; - if (!path.startsWith(WIN32_PIPE_PREFIX)) { - this.path = WIN32_PIPE_PREFIX + path; - } else { - this.path = path; - } - String lockPath = this.path + "_lock"; - lockHandle = API.CreateNamedPipe( - lockPath, - NGWin32NamedPipeLibrary.FILE_FLAG_FIRST_PIPE_INSTANCE | NGWin32NamedPipeLibrary.PIPE_ACCESS_DUPLEX, - 0, - 1, - BUFFER_SIZE, - BUFFER_SIZE, - 0, - null); - if (lockHandle == NGWin32NamedPipeLibrary.INVALID_HANDLE_VALUE) { - throw new IOException(String.format("Could not create lock for %s, error %d", lockPath, API.GetLastError())); - } else { - if (!API.DisconnectNamedPipe(lockHandle)) { - throw new IOException(String.format("Could not disconnect lock %d", API.GetLastError())); - } - } - - } - - public void bind(SocketAddress endpoint) throws IOException { - throw new IOException("Win32 named pipes do not support bind(), pass path to constructor"); - } - - public Socket accept() throws IOException { - HANDLE handle = API.CreateNamedPipe( - path, - NGWin32NamedPipeLibrary.PIPE_ACCESS_DUPLEX | WinNT.FILE_FLAG_OVERLAPPED, - 0, - maxInstances, - BUFFER_SIZE, - BUFFER_SIZE, - 0, - null); - if (handle == NGWin32NamedPipeLibrary.INVALID_HANDLE_VALUE) { - throw new IOException(String.format("Could not create named pipe, error %d", API.GetLastError())); - } - openHandles.add(handle); - - HANDLE connWaitable = API.CreateEvent(null, true, false, null); - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = connWaitable; - olap.write(); - - boolean immediate = API.ConnectNamedPipe(handle, olap.getPointer()); - if (immediate) { - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } - - int connectError = API.GetLastError(); - if (connectError == WinError.ERROR_PIPE_CONNECTED) { - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else if (connectError == WinError.ERROR_NO_DATA) { - // Client has connected and disconnected between CreateNamedPipe() and ConnectNamedPipe() - // connection is broken, but it is returned it avoid loop here. - // Actual error will happen for NGSession when it will try to read/write from/to pipe - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else if (connectError == WinError.ERROR_IO_PENDING) { - if (!API.GetOverlappedResult(handle, olap.getPointer(), new IntByReference(), true)) { - openHandles.remove(handle); - closeOpenPipe(handle); - throw new IOException("GetOverlappedResult() failed for connect operation: " + API.GetLastError()); - } - openHandles.remove(handle); - connectedHandles.add(handle); - return new NGWin32NamedPipeSocket(handle, closeCallback); - } else { - throw new IOException("ConnectNamedPipe() failed with: " + connectError); - } - } - - public void close() throws IOException { - try { - List handlesToClose = new ArrayList<>(); - openHandles.drainTo(handlesToClose); - for (HANDLE handle : handlesToClose) { - closeOpenPipe(handle); - } - - List handlesToDisconnect = new ArrayList<>(); - connectedHandles.drainTo(handlesToDisconnect); - for (HANDLE handle : handlesToDisconnect) { - closeConnectedPipe(handle, true); - } - } finally { - API.CloseHandle(lockHandle); - } - } - - private void closeOpenPipe(HANDLE handle) throws IOException { - API.CancelIoEx(handle, null); - API.CloseHandle(handle); - } - - private void closeConnectedPipe(HANDLE handle, boolean shutdown) throws IOException { - if (!shutdown) { - API.WaitForSingleObject(handle, 10000); - } - API.DisconnectNamedPipe(handle); - API.CloseHandle(handle); - } -} diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java deleted file mode 100644 index b22bb6bbf..000000000 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeSocket.java +++ /dev/null @@ -1,172 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGWin32NamedPipeSocket.java -// Made change in `read` to read just the amount of bytes available. - -/* - - Copyright 2004-2017, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.Memory; -import com.sun.jna.platform.win32.WinBase; -import com.sun.jna.platform.win32.WinError; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.nio.ByteBuffer; - -public class NGWin32NamedPipeSocket extends Socket { - private static final NGWin32NamedPipeLibrary API = NGWin32NamedPipeLibrary.INSTANCE; - private final HANDLE handle; - private final CloseCallback closeCallback; - private final InputStream is; - private final OutputStream os; - private final HANDLE readerWaitable; - private final HANDLE writerWaitable; - - interface CloseCallback { - void onNamedPipeSocketClose(HANDLE handle) throws IOException; - } - - public NGWin32NamedPipeSocket( - HANDLE handle, - NGWin32NamedPipeSocket.CloseCallback closeCallback) throws IOException { - this.handle = handle; - this.closeCallback = closeCallback; - this.readerWaitable = API.CreateEvent(null, true, false, null); - if (readerWaitable == null) { - throw new IOException("CreateEvent() failed "); - } - writerWaitable = API.CreateEvent(null, true, false, null); - if (writerWaitable == null) { - throw new IOException("CreateEvent() failed "); - } - this.is = new NGWin32NamedPipeSocketInputStream(handle); - this.os = new NGWin32NamedPipeSocketOutputStream(handle); - } - - @Override - public InputStream getInputStream() { - return is; - } - - @Override - public OutputStream getOutputStream() { - return os; - } - - @Override - public void close() throws IOException { - closeCallback.onNamedPipeSocketClose(handle); - } - - @Override - public void shutdownInput() throws IOException { - } - - @Override - public void shutdownOutput() throws IOException { - } - - private class NGWin32NamedPipeSocketInputStream extends InputStream { - private final HANDLE handle; - - NGWin32NamedPipeSocketInputStream(HANDLE handle) { - this.handle = handle; - } - - @Override - public int read() throws IOException { - int result; - byte[] b = new byte[1]; - if (read(b) == 0) { - result = -1; - } else { - result = 0xFF & b[0]; - } - return result; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - Memory readBuffer = new Memory(len); - - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = readerWaitable; - olap.write(); - - boolean immediate = API.ReadFile(handle, readBuffer, len, null, olap.getPointer()); - if (!immediate) { - int lastError = API.GetLastError(); - if (lastError != WinError.ERROR_IO_PENDING) { - throw new IOException("ReadFile() failed: " + lastError); - } - } - - IntByReference read = new IntByReference(); - if (!API.GetOverlappedResult(handle, olap.getPointer(), read, true)) { - int lastError = API.GetLastError(); - throw new IOException("GetOverlappedResult() failed for read operation: " + lastError); - } - int actualLen = read.getValue(); - byte[] byteArray = readBuffer.getByteArray(0, actualLen); - System.arraycopy(byteArray, 0, b, off, actualLen); - return actualLen; - } - } - - private class NGWin32NamedPipeSocketOutputStream extends OutputStream { - private final HANDLE handle; - - NGWin32NamedPipeSocketOutputStream(HANDLE handle) { - this.handle = handle; - } - - @Override - public void write(int b) throws IOException { - write(new byte[]{(byte) (0xFF & b)}); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - ByteBuffer data = ByteBuffer.wrap(b, off, len); - - WinBase.OVERLAPPED olap = new WinBase.OVERLAPPED(); - olap.hEvent = writerWaitable; - olap.write(); - - boolean immediate = API.WriteFile(handle, data, len, null, olap.getPointer()); - if (!immediate) { - int lastError = API.GetLastError(); - if (lastError != WinError.ERROR_IO_PENDING) { - throw new IOException("WriteFile() failed: " + lastError); - } - } - IntByReference written = new IntByReference(); - if (!API.GetOverlappedResult(handle, olap.getPointer(), written, true)) { - int lastError = API.GetLastError(); - throw new IOException("GetOverlappedResult() failed for write operation: " + lastError); - } - if (written.getValue() != len) { - throw new IOException("WriteFile() wrote less bytes than requested"); - } - } - } -} diff --git a/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java b/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java deleted file mode 100644 index 7fb5d9d53..000000000 --- a/main-command/src/main/java/sbt/internal/ReferenceCountedFileDescriptor.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copied from https://github.com/facebook/nailgun/blob/af623fddedfdca010df46302a0711ce0e2cc1ba6/nailgun-server/src/main/java/com/martiansoftware/nailgun/ReferenceCountedFileDescriptor.java - -/* - - Copyright 2004-2015, Martian Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package sbt.internal; - -import com.sun.jna.LastErrorException; - -import java.io.IOException; - -/** - * Encapsulates a file descriptor plus a reference count to ensure close requests - * only close the file descriptor once the last reference to the file descriptor - * is released. - * - * If not explicitly closed, the file descriptor will be closed when - * this object is finalized. - */ -public class ReferenceCountedFileDescriptor { - private int fd; - private int fdRefCount; - private boolean closePending; - - public ReferenceCountedFileDescriptor(int fd) { - this.fd = fd; - this.fdRefCount = 0; - this.closePending = false; - } - - protected void finalize() throws IOException { - close(); - } - - public synchronized int acquire() { - fdRefCount++; - return fd; - } - - public synchronized void release() throws IOException { - fdRefCount--; - if (fdRefCount == 0 && closePending && fd != -1) { - doClose(); - } - } - - public synchronized void close() throws IOException { - if (fd == -1 || closePending) { - return; - } - - if (fdRefCount == 0) { - doClose(); - } else { - // Another thread has the FD. We'll close it when they release the reference. - closePending = true; - } - } - - private void doClose() throws IOException { - try { - NGUnixDomainSocketLibrary.close(fd); - fd = -1; - } catch (LastErrorException e) { - throw new IOException(e); - } - } -} diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 521c8d399..f45019fb5 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -25,6 +25,7 @@ import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter } import sbt.internal.protocol.codec._ import sbt.internal.util.ErrorHandling import sbt.internal.util.Util.isWindows +import org.scalasbt.ipcsocket._ private[sbt] sealed trait ServerInstance { def shutdown(): Unit @@ -57,11 +58,11 @@ private[sbt] object Server { connection.connectionType match { case ConnectionType.Local if isWindows => // Named pipe already has an exclusive lock. - addServerError(new NGWin32NamedPipeServerSocket(pipeName)) + addServerError(new Win32NamedPipeServerSocket(pipeName)) case ConnectionType.Local => - tryClient(new NGUnixDomainSocket(socketfile.getAbsolutePath)) + tryClient(new UnixDomainSocket(socketfile.getAbsolutePath)) prepareSocketfile() - addServerError(new NGUnixDomainServerSocket(socketfile.getAbsolutePath)) + addServerError(new UnixDomainServerSocket(socketfile.getAbsolutePath)) case ConnectionType.Tcp => tryClient(new Socket(InetAddress.getByName(host), port)) addServerError(new ServerSocket(port, 50, InetAddress.getByName(host))) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f8d470ccd..5ad8a4393 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -33,6 +33,7 @@ object Dependencies { val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.2" val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.2" val testInterface = "org.scala-sbt" % "test-interface" % "1.0" + val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.0.0" private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion From bd0e44c2929ef34a7c83bf5f98df711e0df0dcb6 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 24 Jan 2018 03:56:42 -0500 Subject: [PATCH 11/21] start an instance of sbt in the background --- .appveyor.yml | 2 +- .travis.yml | 2 +- build.sbt | 5 +- .../scala/sbt/protocol/ClientSocket.scala | 45 +++++ .../sbt-test/server/handshake/Client.scala | 106 ----------- sbt/src/sbt-test/server/handshake/build.sbt | 15 -- sbt/src/sbt-test/server/handshake/test | 6 - sbt/src/server-test/handshake/build.sbt | 6 + .../test/scala/sbt/RunFromSourceMain.scala | 2 +- sbt/src/test/scala/sbt/ServerSpec.scala | 179 ++++++++++++++++++ 10 files changed, 237 insertions(+), 131 deletions(-) create mode 100644 protocol/src/main/scala/sbt/protocol/ClientSocket.scala delete mode 100644 sbt/src/sbt-test/server/handshake/Client.scala delete mode 100644 sbt/src/sbt-test/server/handshake/build.sbt delete mode 100644 sbt/src/sbt-test/server/handshake/test create mode 100644 sbt/src/server-test/handshake/build.sbt create mode 100644 sbt/src/test/scala/sbt/ServerSpec.scala diff --git a/.appveyor.yml b/.appveyor.yml index a0d3292f1..7bc9e8b57 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,4 +20,4 @@ install: - SET PATH=C:\sbt\sbt\bin;%PATH% - SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g -Dfile.encoding=UTF8 test_script: - - sbt "scripted actions/* server/*" + - sbt "scripted actions/*" "testOnly sbt.ServerSpec" diff --git a/.travis.yml b/.travis.yml index 4d33b9c23..85db736d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ env: - SBT_CMD="scripted dependency-management/*4of4" - SBT_CMD="scripted java/* package/* reporter/* run/* project-load/*" - SBT_CMD="scripted project/*1of2" - - SBT_CMD="scripted project/*2of2 server/*" + - SBT_CMD="scripted project/*2of2" - SBT_CMD="scripted source-dependencies/*1of3" - SBT_CMD="scripted source-dependencies/*2of3" - SBT_CMD="scripted source-dependencies/*3of3" diff --git a/build.sbt b/build.sbt index 17d4b477e..0dca48aae 100644 --- a/build.sbt +++ b/build.sbt @@ -294,6 +294,7 @@ lazy val actionsProj = (project in file("main-actions")) lazy val protocolProj = (project in file("protocol")) .enablePlugins(ContrabandPlugin, JsonCodecPlugin) + .dependsOn(collectionProj) .settings( testedBaseSettings, name := "Protocol", @@ -441,7 +442,7 @@ lazy val sbtProj = (project in file("sbt")) .dependsOn(mainProj, scriptedSbtProj % "test->test") .enablePlugins(BuildInfoPlugin) .settings( - baseSettings, + testedBaseSettings, name := "sbt", normalizedName := "sbt", crossScalaVersions := Seq(baseScalaVersion), @@ -453,6 +454,8 @@ lazy val sbtProj = (project in file("sbt")) buildInfoObject in Test := "TestBuildInfo", buildInfoKeys in Test := Seq[BuildInfoKey](fullClasspath in Compile), connectInput in run in Test := true, + outputStrategy in run in Test := Some(StdoutOutput), + fork in Test := true, ) .configure(addSbtCompilerBridge) diff --git a/protocol/src/main/scala/sbt/protocol/ClientSocket.scala b/protocol/src/main/scala/sbt/protocol/ClientSocket.scala new file mode 100644 index 000000000..df155d439 --- /dev/null +++ b/protocol/src/main/scala/sbt/protocol/ClientSocket.scala @@ -0,0 +1,45 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt +package protocol + +import java.io.File +import java.net.{ Socket, URI, InetAddress } +import sjsonnew.BasicJsonProtocol +import sjsonnew.support.scalajson.unsafe.{ Parser, Converter } +import sjsonnew.shaded.scalajson.ast.unsafe.JValue +import sbt.internal.protocol.{ PortFile, TokenFile } +import sbt.internal.protocol.codec.{ PortFileFormats, TokenFileFormats } +import sbt.internal.util.Util.isWindows +import org.scalasbt.ipcsocket._ + +object ClientSocket { + private lazy val fileFormats = new BasicJsonProtocol with PortFileFormats with TokenFileFormats {} + + def socket(portfile: File): (Socket, Option[String]) = { + import fileFormats._ + val json: JValue = Parser.parseFromFile(portfile).get + val p = Converter.fromJson[PortFile](json).get + val uri = new URI(p.uri) + // println(uri) + val token = p.tokenfilePath map { tp => + val tokeFile = new File(tp) + val json: JValue = Parser.parseFromFile(tokeFile).get + val t = Converter.fromJson[TokenFile](json).get + t.token + } + val sk = uri.getScheme match { + case "local" if isWindows => + new Win32NamedPipeSocket("""\\.\pipe\""" + uri.getSchemeSpecificPart) + case "local" => new UnixDomainSocket(uri.getSchemeSpecificPart) + case "tcp" => new Socket(InetAddress.getByName(uri.getHost), uri.getPort) + case _ => sys.error(s"Unsupported uri: $uri") + } + (sk, token) + } +} diff --git a/sbt/src/sbt-test/server/handshake/Client.scala b/sbt/src/sbt-test/server/handshake/Client.scala deleted file mode 100644 index 2f41f4c18..000000000 --- a/sbt/src/sbt-test/server/handshake/Client.scala +++ /dev/null @@ -1,106 +0,0 @@ -package example - -import java.net.{ URI, Socket, InetAddress, SocketException } -import sbt.io._ -import sbt.io.syntax._ -import java.io.File -import sjsonnew.support.scalajson.unsafe.{ Parser, Converter, CompactPrinter } -import sjsonnew.shaded.scalajson.ast.unsafe.{ JValue, JObject, JString } - -object Client extends App { - val host = "127.0.0.1" - val delimiter: Byte = '\n'.toByte - - lazy val connection = getConnection - lazy val out = connection.getOutputStream - lazy val in = connection.getInputStream - - val t = getToken - val msg0 = s"""{ "type": "InitCommand", "token": "$t" }""" - - writeLine(s"Content-Length: ${ msg0.size + 2 }") - writeLine("Content-Type: application/sbt-x1") - writeLine("") - writeLine(msg0) - out.flush - - writeLine("Content-Length: 49") - writeLine("Content-Type: application/sbt-x1") - writeLine("") - // 12345678901234567890123456789012345678901234567890 - writeLine("""{ "type": "ExecCommand", "commandLine": "exit" }""") - writeLine("") - out.flush - - val baseDirectory = new File(args(0)) - IO.write(baseDirectory / "ok.txt", "ok") - - def getToken: String = { - val tokenfile = new File(getTokenFileUri) - val json: JValue = Parser.parseFromFile(tokenfile).get - json match { - case JObject(fields) => - (fields find { _.field == "token" } map { _.value }) match { - case Some(JString(value)) => value - case _ => - sys.error("json doesn't token field that is JString") - } - case _ => sys.error("json doesn't have token field") - } - } - - def getTokenFileUri: URI = { - val portfile = baseDirectory / "project" / "target" / "active.json" - val json: JValue = Parser.parseFromFile(portfile).get - json match { - case JObject(fields) => - (fields find { _.field == "tokenfileUri" } map { _.value }) match { - case Some(JString(value)) => new URI(value) - case _ => - sys.error("json doesn't tokenfile field that is JString") - } - case _ => sys.error("json doesn't have tokenfile field") - } - } - - def getPort: Int = { - val portfile = baseDirectory / "project" / "target" / "active.json" - val json: JValue = Parser.parseFromFile(portfile).get - json match { - case JObject(fields) => - (fields find { _.field == "uri" } map { _.value }) match { - case Some(JString(value)) => - val u = new URI(value) - u.getPort - case _ => - sys.error("json doesn't uri field that is JString") - } - case _ => sys.error("json doesn't have uri field") - } - } - - def getConnection: Socket = - try { - new Socket(InetAddress.getByName(host), getPort) - } catch { - case _ => - Thread.sleep(1000) - getConnection - } - - def writeLine(s: String): Unit = { - if (s != "") { - out.write(s.getBytes("UTF-8")) - } - writeEndLine - } - - def writeEndLine(): Unit = { - val retByte: Byte = '\r'.toByte - val delimiter: Byte = '\n'.toByte - - out.write(retByte.toInt) - out.write(delimiter.toInt) - out.flush - } -} diff --git a/sbt/src/sbt-test/server/handshake/build.sbt b/sbt/src/sbt-test/server/handshake/build.sbt deleted file mode 100644 index 851648f3c..000000000 --- a/sbt/src/sbt-test/server/handshake/build.sbt +++ /dev/null @@ -1,15 +0,0 @@ -lazy val runClient = taskKey[Unit]("") - -lazy val root = (project in file(".")) - .settings( - serverConnectionType in Global := ConnectionType.Tcp, - scalaVersion := "2.12.3", - serverPort in Global := 5123, - libraryDependencies += "org.scala-sbt" %% "io" % "1.0.1", - libraryDependencies += "com.eed3si9n" %% "sjson-new-scalajson" % "0.8.0", - runClient := (Def.taskDyn { - val b = baseDirectory.value - (bgRun in Compile).toTask(s""" $b""") - }).value - ) - \ No newline at end of file diff --git a/sbt/src/sbt-test/server/handshake/test b/sbt/src/sbt-test/server/handshake/test deleted file mode 100644 index 703942376..000000000 --- a/sbt/src/sbt-test/server/handshake/test +++ /dev/null @@ -1,6 +0,0 @@ -> show serverPort -> runClient - --> shell - -$ exists ok.txt diff --git a/sbt/src/server-test/handshake/build.sbt b/sbt/src/server-test/handshake/build.sbt new file mode 100644 index 000000000..192730eef --- /dev/null +++ b/sbt/src/server-test/handshake/build.sbt @@ -0,0 +1,6 @@ +lazy val root = (project in file(".")) + .settings( + Global / serverLog / logLevel := Level.Debug, + name := "handshake", + scalaVersion := "2.12.3", + ) diff --git a/sbt/src/test/scala/sbt/RunFromSourceMain.scala b/sbt/src/test/scala/sbt/RunFromSourceMain.scala index be79cc54a..27cba48fe 100644 --- a/sbt/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt/src/test/scala/sbt/RunFromSourceMain.scala @@ -22,7 +22,7 @@ object RunFromSourceMain { // this arrangement is because Scala does not always properly optimize away // the tail recursion in a catch statement - @tailrec private def run(baseDir: File, args: Seq[String]): Unit = + @tailrec private[sbt] def run(baseDir: File, args: Seq[String]): Unit = runImpl(baseDir, args) match { case Some((baseDir, args)) => run(baseDir, args) case None => () diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala new file mode 100644 index 000000000..648d88203 --- /dev/null +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -0,0 +1,179 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt + +import org.scalatest._ +import scala.concurrent._ +import java.io.{ InputStream, OutputStream } +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.{ ThreadFactory, ThreadPoolExecutor } +import sbt.protocol.ClientSocket + +class ServerSpec extends AsyncFlatSpec with Matchers { + import ServerSpec._ + + "server" should "start" in { + withBuildSocket("handshake") { (out, in, tkn) => + writeLine( + """{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/name" } }""", + out) + Thread.sleep(100) + val l2 = contentLength(in) + println(l2) + readLine(in) + readLine(in) + val x2 = readContentLength(in, l2) + println(x2) + assert(1 == 1) + } + } +} + +object ServerSpec { + private val serverTestBase: File = new File(".").getAbsoluteFile / "sbt" / "src" / "server-test" + private val nextThreadId = new AtomicInteger(1) + private val threadGroup = Thread.currentThread.getThreadGroup() + val readBuffer = new Array[Byte](4096) + var buffer: Vector[Byte] = Vector.empty + var bytesRead = 0 + private val delimiter: Byte = '\n'.toByte + private val RetByte = '\r'.toByte + + private val threadFactory = new ThreadFactory() { + override def newThread(runnable: Runnable): Thread = { + val thread = + new Thread(threadGroup, + runnable, + s"sbt-test-server-threads-${nextThreadId.getAndIncrement}") + // Do NOT setDaemon because then the code in TaskExit.scala in sbt will insta-kill + // the backgrounded process, at least for the case of the run task. + thread + } + } + + private val executor = new ThreadPoolExecutor( + 0, /* corePoolSize */ + 1, /* maxPoolSize, max # of servers */ + 2, + java.util.concurrent.TimeUnit.SECONDS, + /* keep alive unused threads this long (if corePoolSize < maxPoolSize) */ + new java.util.concurrent.SynchronousQueue[Runnable](), + threadFactory + ) + + def backgroundRun(baseDir: File, args: Seq[String]): Unit = { + executor.execute(new Runnable { + def run(): Unit = { + RunFromSourceMain.run(baseDir, args) + } + }) + } + + def shutdown(): Unit = executor.shutdown() + + def withBuildSocket(testBuild: String)( + f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = { + IO.withTemporaryDirectory { temp => + IO.copyDirectory(serverTestBase / testBuild, temp / testBuild) + withBuildSocket(temp / testBuild)(f) + } + } + + def sendJsonRpc(message: String, out: OutputStream): Unit = { + writeLine(s"""Content-Length: ${message.size + 2}""", out) + writeLine("", out) + writeLine(message, out) + } + + def contentLength(in: InputStream): Int = { + readLine(in) map { line => + line.drop(16).toInt + } getOrElse (0) + } + + def readLine(in: InputStream): Option[String] = { + if (buffer.isEmpty) { + val bytesRead = in.read(readBuffer) + if (bytesRead > 0) { + buffer = buffer ++ readBuffer.toVector.take(bytesRead) + } + } + val delimPos = buffer.indexOf(delimiter) + if (delimPos > 0) { + val chunk0 = buffer.take(delimPos) + buffer = buffer.drop(delimPos + 1) + // remove \r at the end of line. + if (chunk0.size > 0 && chunk0.indexOf(RetByte) == chunk0.size - 1) + Some(new String(chunk0.dropRight(1).toArray, "utf-8")) + else Some(new String(chunk0.toArray, "utf-8")) + } else None // no EOL yet, so skip this turn. + } + + def readContentLength(in: InputStream, length: Int): Option[String] = { + if (buffer.isEmpty) { + val bytesRead = in.read(readBuffer) + if (bytesRead > 0) { + buffer = buffer ++ readBuffer.toVector.take(bytesRead) + } + } + if (length <= buffer.size) { + val chunk = buffer.take(length) + buffer = buffer.drop(length) + Some(new String(chunk.toArray, "utf-8")) + } else None // have not read enough yet, so skip this turn. + } + + def writeLine(s: String, out: OutputStream): Unit = { + def writeEndLine(): Unit = { + val retByte: Byte = '\r'.toByte + val delimiter: Byte = '\n'.toByte + out.write(retByte.toInt) + out.write(delimiter.toInt) + out.flush + } + + if (s != "") { + out.write(s.getBytes("UTF-8")) + } + writeEndLine + } + + def withBuildSocket(baseDirectory: File)( + f: (OutputStream, InputStream, Option[String]) => Future[Assertion]): Future[Assertion] = { + backgroundRun(baseDirectory, Nil) + + val portfile = baseDirectory / "project" / "target" / "active.json" + + def waitForPortfile(n: Int): Unit = + if (portfile.exists) () + else { + if (n <= 0) sys.error(s"Timeout. $portfile is not found.") + else { + Thread.sleep(1000) + waitForPortfile(n - 1) + } + } + waitForPortfile(10) + val (sk, tkn) = ClientSocket.socket(portfile) + val out = sk.getOutputStream + val in = sk.getInputStream + + sendJsonRpc( + """{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }""", + out) + + try { + f(out, in, tkn) + } finally { + sendJsonRpc( + """{ "jsonrpc": "2.0", "id": 9, "method": "sbt/exec", "params": { "commandLine": "exit" } }""", + out) + shutdown() + } + } +} From 5ab122c3cfcb78f21868c89a45b63acd7d4e7841 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 30 Jan 2018 00:42:02 -0500 Subject: [PATCH 12/21] Use State to pick the port file --- .../scala/sbt/internal/CommandExchange.scala | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index d7b52280d..bdf477510 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -131,47 +131,44 @@ private[sbt] final class CommandExchange { new NetworkChannel(name, socket, Project structure s, auth, instance, logger) subscribe(channel) } - server match { - case Some(_) => // do nothing - case None if !firstInstance.get => // there's another server - case _ => - val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json" - val h = Hash.halfHashString(IO.toURI(portfile).toString) - val tokenfile = BuildPaths.getGlobalBase(s) / "server" / h / "token.json" - val socketfile = BuildPaths.getGlobalBase(s) / "server" / h / "sock" - val pipeName = "sbt-server-" + h - val connection = - ServerConnection(connectionType, - host, - port, - auth, - portfile, - tokenfile, - socketfile, - pipeName) - val x = Server.start(connection, onIncomingSocket, s.log) - - // don't throw exception when it times out - val d = "10s" - Try(Await.ready(x.ready, Duration(d))) - x.ready.value match { - case Some(Success(_)) => - // rememeber to shutdown only when the server comes up - server = Some(x) - case Some(Failure(e: AlreadyRunningException)) => - s.log.warn( - "sbt server could not start because there's another instance of sbt running on this build.") - s.log.warn("Running multiple instances is unsupported") - server = None - firstInstance.set(false) - case Some(Failure(e)) => - s.log.error(e.toString) - server = None - case None => - s.log.warn(s"sbt server could not start in $d") - server = None - firstInstance.set(false) - } + if (server.isEmpty && firstInstance.get) { + val portfile = s.baseDir / "project" / "target" / "active.json" + val h = Hash.halfHashString(IO.toURI(portfile).toString) + val tokenfile = BuildPaths.getGlobalBase(s) / "server" / h / "token.json" + val socketfile = BuildPaths.getGlobalBase(s) / "server" / h / "sock" + val pipeName = "sbt-server-" + h + val connection = ServerConnection( + connectionType, + host, + port, + auth, + portfile, + tokenfile, + socketfile, + pipeName, + ) + val serverInstance = Server.start(connection, onIncomingSocket, s.log) + // don't throw exception when it times out + val d = "10s" + Try(Await.ready(serverInstance.ready, Duration(d))) + serverInstance.ready.value match { + case Some(Success(())) => + // remember to shutdown only when the server comes up + server = Some(serverInstance) + case Some(Failure(_: AlreadyRunningException)) => + s.log.warn( + "sbt server could not start because there's another instance of sbt running on this build.") + s.log.warn("Running multiple instances is unsupported") + server = None + firstInstance.set(false) + case Some(Failure(e)) => + s.log.error(e.toString) + server = None + case None => + s.log.warn(s"sbt server could not start in $d") + server = None + firstInstance.set(false) + } } s } From 39549855067c8c6a2de30d976f1564677c3f11a8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 7 Feb 2018 18:16:02 -0500 Subject: [PATCH 13/21] formatting --- main/src/main/scala/sbt/Extracted.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/Extracted.scala b/main/src/main/scala/sbt/Extracted.scala index f507fa2c7..4ed0e0c8b 100644 --- a/main/src/main/scala/sbt/Extracted.scala +++ b/main/src/main/scala/sbt/Extracted.scala @@ -120,7 +120,9 @@ final case class Extracted(structure: BuildStructure, structure.data.get(scope, key) getOrElse sys.error( display.show(ScopedKey(scope, key)) + " is undefined.") - @deprecated("This discards session settings. Migrate to appendWithSession or appendWithoutSession.", "1.2.0") + @deprecated( + "This discards session settings. Migrate to appendWithSession or appendWithoutSession.", + "1.2.0") def append(settings: Seq[Setting[_]], state: State): State = appendWithoutSession(settings, state) From 23cdfd8369925c5640a4d77d914e07b551263d5e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 31 Jan 2018 00:04:23 -0500 Subject: [PATCH 14/21] improve Windows build --- .appveyor.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7bc9e8b57..ccc70ab7c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,9 +4,8 @@ init: - git config --global core.autocrlf input install: - - cinst jdk8 -params 'installdir=C:\\jdk8' - - SET JAVA_HOME=C:\jdk8 - - SET PATH=C:\jdk8\bin;%PATH% + - SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0 + - SET PATH=%JAVA_HOME%\bin;%PATH% - ps: | Add-Type -AssemblyName System.IO.Compression.FileSystem @@ -21,3 +20,7 @@ install: - SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g -Dfile.encoding=UTF8 test_script: - sbt "scripted actions/*" "testOnly sbt.ServerSpec" + +cache: + - '%USERPROFILE%\.ivy2\cache' + - '%USERPROFILE%\.sbt' From 639ee220dc3c641c7e608e0a03813500ef1c2d50 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 31 Jan 2018 10:20:33 +0000 Subject: [PATCH 15/21] Fix Codacy issue in ServerSpec --- sbt/src/test/scala/sbt/ServerSpec.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala index 648d88203..9adeabcc8 100644 --- a/sbt/src/test/scala/sbt/ServerSpec.scala +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -108,9 +108,8 @@ object ServerSpec { val chunk0 = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) // remove \r at the end of line. - if (chunk0.size > 0 && chunk0.indexOf(RetByte) == chunk0.size - 1) - Some(new String(chunk0.dropRight(1).toArray, "utf-8")) - else Some(new String(chunk0.toArray, "utf-8")) + val chunk1 = if (chunk0.isEmpty || chunk0.last != RetByte) chunk0 else chunk0.dropRight(1) + Some(new String(chunk1.toArray, "utf-8")) } else None // no EOL yet, so skip this turn. } From 7df32cff849e4c9ef1412d979628fe20702b73d0 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 8 Feb 2018 01:06:09 -0500 Subject: [PATCH 16/21] Zinc 1.1.1, LM 1.1.3, IO 1.1.4 --- project/Dependencies.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fd613b0a7..a8bb47140 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -8,10 +8,10 @@ object Dependencies { val baseScalaVersion = scala212 // sbt modules - private val ioVersion = "1.1.3" + private val ioVersion = "1.1.4" private val utilVersion = "1.1.2" - private val lmVersion = "1.1.2" - private val zincVersion = "1.1.0" + private val lmVersion = "1.1.3" + private val zincVersion = "1.1.1" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion From 402378a81e9a8d4981e04bc6d72f9fee04a90156 Mon Sep 17 00:00:00 2001 From: Dale Wijnand <344610+dwijnand@users.noreply.github.com> Date: Wed, 7 Feb 2018 09:22:42 +0000 Subject: [PATCH 17/21] Adds sbt.boot.lock sysprop to opt-out Adds sbt.boot.lock as a system property to opt-out of locking. This might be useful when running on filesystems on which the locking code path would throw an exception. Upstream issue: https://bugs.openjdk.java.net/browse/JDK-8193594 Provides a workaround for #2222, thus... Closes #2222. --- launch/src/main/input_resources/sbt/sbt.boot.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/launch/src/main/input_resources/sbt/sbt.boot.properties b/launch/src/main/input_resources/sbt/sbt.boot.properties index cd46ae8af..f13961dcf 100644 --- a/launch/src/main/input_resources/sbt/sbt.boot.properties +++ b/launch/src/main/input_resources/sbt/sbt.boot.properties @@ -22,6 +22,7 @@ [boot] directory: ${sbt.boot.directory-${sbt.global.base-${user.home}/.sbt}/boot/} + lock: ${sbt.boot.lock-true} [ivy] ivy-home: ${sbt.ivy.home-${user.home}/.ivy2/} From 74ff6aa1ceb8029d9bc9c422fb179a3c1d89f51f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 8 Feb 2018 14:16:14 +0000 Subject: [PATCH 18/21] Fix another Codacy issue in ServerSpec --- sbt/src/test/scala/sbt/ServerSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbt/src/test/scala/sbt/ServerSpec.scala b/sbt/src/test/scala/sbt/ServerSpec.scala index 9adeabcc8..7ad307fd8 100644 --- a/sbt/src/test/scala/sbt/ServerSpec.scala +++ b/sbt/src/test/scala/sbt/ServerSpec.scala @@ -108,7 +108,7 @@ object ServerSpec { val chunk0 = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) // remove \r at the end of line. - val chunk1 = if (chunk0.isEmpty || chunk0.last != RetByte) chunk0 else chunk0.dropRight(1) + val chunk1 = if (chunk0.lastOption contains RetByte) chunk0.dropRight(1) else chunk0 Some(new String(chunk1.toArray, "utf-8")) } else None // no EOL yet, so skip this turn. } From 4da6a8952b8f52962dafb89e6ca572852e946b53 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 8 Feb 2018 13:08:21 -0500 Subject: [PATCH 19/21] 1.1.1 launchconfig --- src/main/conscript/scalas/launchconfig | 2 +- src/main/conscript/screpl/launchconfig | 2 +- src/main/conscript/xsbt/launchconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/conscript/scalas/launchconfig b/src/main/conscript/scalas/launchconfig index 98febc38c..61b9b5108 100644 --- a/src/main/conscript/scalas/launchconfig +++ b/src/main/conscript/scalas/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[1.1.0]} + version: ${sbt.version-read(sbt.version)[1.1.1]} class: sbt.ScriptMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} diff --git a/src/main/conscript/screpl/launchconfig b/src/main/conscript/screpl/launchconfig index 17a32efb0..23a9a6a39 100644 --- a/src/main/conscript/screpl/launchconfig +++ b/src/main/conscript/screpl/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[1.1.0]} + version: ${sbt.version-read(sbt.version)[1.1.1]} class: sbt.ConsoleMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} diff --git a/src/main/conscript/xsbt/launchconfig b/src/main/conscript/xsbt/launchconfig index 79ddd5ef7..ff2abceab 100644 --- a/src/main/conscript/xsbt/launchconfig +++ b/src/main/conscript/xsbt/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[1.1.0]} + version: ${sbt.version-read(sbt.version)[1.1.1]} class: sbt.xMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} From 39d992717929f8d5f935b727c8ee095ed726ca1f Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 8 Feb 2018 13:08:32 -0500 Subject: [PATCH 20/21] 1.1.2-SNAPSHOT --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 0dca48aae..59cf7394c 100644 --- a/build.sbt +++ b/build.sbt @@ -9,7 +9,7 @@ def buildLevelSettings: Seq[Setting[_]] = inThisBuild( Seq( organization := "org.scala-sbt", - version := "1.1.1-SNAPSHOT", + version := "1.1.2-SNAPSHOT", description := "sbt is an interactive build tool", bintrayOrganization := Some("sbt"), bintrayRepository := { From 15018c1971a2adfaea922c3ec82218cd713f73d9 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 9 Feb 2018 23:33:08 -0500 Subject: [PATCH 21/21] notes --- notes/1.1.1.markdown | 45 +++++++++++++++++++++++++++++ notes/1.1.1/autoStartServer.md | 31 -------------------- notes/1.1.1/console_jline.md | 13 --------- notes/1.1.1/fix-Extracted.append.md | 11 ------- 4 files changed, 45 insertions(+), 55 deletions(-) create mode 100644 notes/1.1.1.markdown delete mode 100644 notes/1.1.1/autoStartServer.md delete mode 100644 notes/1.1.1/console_jline.md delete mode 100644 notes/1.1.1/fix-Extracted.append.md diff --git a/notes/1.1.1.markdown b/notes/1.1.1.markdown new file mode 100644 index 000000000..58cba0a73 --- /dev/null +++ b/notes/1.1.1.markdown @@ -0,0 +1,45 @@ +### Fixes + +- Fixes "Modified names for (class) is empty" error. [zinc#292][zinc292] / [zinc#484][zinc484] by [@jvican][@jvican] +- Fixes tab completion in `console` while running in batch mode as `sbt console`. [#3841][3841]/[#3876][3876] by [@eed3si9n][@eed3si9n] +- Fixes file timestamp retrieval of missing files on Windows. [#3871][3871] / [io#120][io120] by [@cunei][@cunei] +- Aligns the errors thrown by file timestamp implementations. Fixes [#3894][3894] / [io#121][io121] by [@j-keck][@j-keck] +- Adds file timestamps native support for FreeBSD. [#3894][3894] / [io#124][io124] by [@cunei][@cunei] +- Fixes JDK 10 version string parsing. [launcher#209][launcher209] by [@2m][@2m] + +### Improvements + +- Deprecates `Extracted#append` in favour of `appendWithSession` or `appendWithoutSession`. [#3865][3865] by [@dwijnand][@dwijnand] +- Adds a new global `Boolean` setting called `autoStartServer`. See below. +- Upgrades Scala versions used for sbt cross building `^^`. [#3923][3923] by [@dwijnand][@dwijnand] +- Many documentation maintenance changes by [@xuwei-k][@xuwei-k] + +### autoStartServer setting + +sbt 1.1.1 adds a new global `Boolean` setting called `autoStartServer`, which is set to `true` by default. +When set to `true`, sbt shell will automatically start sbt server. Otherwise, it will not start the server until `startSever` command is issued. This could be used to opt out of server for security reasons. + +[#3922][3922] by [@swaldman][@swaldman] + + [@eed3si9n]: https://github.com/eed3si9n + [@dwijnand]: http://github.com/dwijnand + [@cunei]: https://github.com/cunei + [@jvican]: https://github.com/jvican + [@Duhemm]: https://github.com/Duhemm + [@j-keck]: https://github.com/j-keck + [@swaldman]: https://github.com/swaldman + [@xuwei-k]: https://github.com/xuwei-k + [@2m]: https://github.com/2m + [3871]: https://github.com/sbt/sbt/issues/3871 + [io120]: https://github.com/sbt/io/pull/120 + [3894]: https://github.com/sbt/sbt/issues/3894 + [io121]: https://github.com/sbt/io/pull/121 + [io124]: https://github.com/sbt/io/pull/124 + [zinc292]: https://github.com/sbt/zinc/issues/292 + [zinc484]: https://github.com/sbt/zinc/pull/484 + [3865]: https://github.com/sbt/sbt/pull/3865 + [3841]: https://github.com/sbt/sbt/issues/3841 + [3876]: https://github.com/sbt/sbt/pull/3876 + [3923]: https://github.com/sbt/sbt/pull/3923 + [3922]: https://github.com/sbt/sbt/pull/3922 + [launcher209]: https://github.com/sbt/sbt-launcher-package/pull/209 diff --git a/notes/1.1.1/autoStartServer.md b/notes/1.1.1/autoStartServer.md deleted file mode 100644 index cc00c3fbc..000000000 --- a/notes/1.1.1/autoStartServer.md +++ /dev/null @@ -1,31 +0,0 @@ -### Improvements - -This pull request implements a Boolean setting called `autoStartServer`, whose default value is `true'. - -If a build or plugin explicitly sets it to `false`, the sbt-1.x server will not start up -(exactly as if the system property `sbt.server.autostart` were set to `false`). - -Users who set `autoStartServer` to `false` may manually execute `startServer` at the interactive prompt, -if they wish to use the server during a shell session. - -### Motivation - -Projects often encounter private information, such as deployment credentials, private keys, etc. -For such projects, it may be preferable to reduce the potential attack surface than to enjoy the -interoperability offered by sbt's server. Projects that wish to make this tradeoff can set `autoStartServer` -to `false` in their build. Security-sensitive plugins can disable `autoStartServer` as well, modifying the -default behavior in favor of security. - -(My own motivation is that I am working on a [plugin for developing Ethereum applications](https://github.com/swaldman/sbt-ethereum) -with scala and sbt. It must work with extremely sensitive private keys.) - ---- - -See also a [recent conversation on Stack Exchange](https://stackoverflow.com/questions/48591179/can-one-disable-the-sbt-1-x-server/48593906#48593906). - ---- - -##### History - -2018-02-06 Modified from negative `suppressServer` to positive `autoStartServer` at the (sensible) request of @eed3si9n - diff --git a/notes/1.1.1/console_jline.md b/notes/1.1.1/console_jline.md deleted file mode 100644 index fd851ce16..000000000 --- a/notes/1.1.1/console_jline.md +++ /dev/null @@ -1,13 +0,0 @@ - [@eed3si9n]: https://github.com/eed3si9n - - [3841]: https://github.com/sbt/sbt/issues/3841 - [3876]: https://github.com/sbt/sbt/pull/3876 - -### Fixes with compatibility implications - -### Improvements - - -### Bug fixes - -- Fixes tab completion in `console` while running in batch mode as `sbt console`. [#3841][3841]/[#3876][3876] by [@eed3si9n][@eed3si9n] diff --git a/notes/1.1.1/fix-Extracted.append.md b/notes/1.1.1/fix-Extracted.append.md deleted file mode 100644 index 56e450391..000000000 --- a/notes/1.1.1/fix-Extracted.append.md +++ /dev/null @@ -1,11 +0,0 @@ -[@dwijnand]: https://github.com/dwijnand - -[#3865]: https://github.com/sbt/sbt/pull/3865 - -### Fixes with compatibility implications - -### Improvements - -- Deprecates `Extracted#append` in favour of `appendWithSession` or `appendWithoutSession`. [#3865][] by [@dwijnand][] - -### Bug fixes