diff --git a/build.sbt b/build.sbt index adb4afef7..d1d42a013 100644 --- a/build.sbt +++ b/build.sbt @@ -917,6 +917,7 @@ lazy val mainProj = (project in file("main")) case v if v.startsWith("2.12.") => List(compilerPlugin(silencerPlugin)) case _ => List() }), + libraryDependencies += "com.swoval" % "file-tree-views" % "2.1.6", managedSourceDirectories in Compile += baseDirectory.value / "src" / "main" / "contraband-scala", sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", diff --git a/main-command/src/main/java/sbt/internal/BootServerSocket.java b/main-command/src/main/java/sbt/internal/BootServerSocket.java index afae5c6f6..fab6c90d2 100644 --- a/main-command/src/main/java/sbt/internal/BootServerSocket.java +++ b/main-command/src/main/java/sbt/internal/BootServerSocket.java @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import net.openhft.hashing.LongHashFunction; import org.scalasbt.ipcsocket.UnixDomainServerSocket; -import org.scalasbt.ipcsocket.UnixDomainSocket; import org.scalasbt.ipcsocket.Win32NamedPipeServerSocket; import org.scalasbt.ipcsocket.Win32NamedPipeSocket; import org.scalasbt.ipcsocket.Win32SecurityLevel; @@ -337,18 +336,24 @@ public class BootServerSocket implements AutoCloseable { static ServerSocket newSocket(final String sock) throws ServerAlreadyBootingException { ServerSocket socket = null; String name = socketName(sock); + boolean jni = requiresJNI() || System.getProperty("sbt.ipcsocket.jni", "false").equals("true"); try { if (!isWindows) Files.deleteIfExists(Paths.get(sock)); socket = isWindows - ? new Win32NamedPipeServerSocket(name, false, Win32SecurityLevel.OWNER_DACL) - : new UnixDomainServerSocket(name); + ? new Win32NamedPipeServerSocket(name, jni, Win32SecurityLevel.OWNER_DACL) + : new UnixDomainServerSocket(name, jni); return socket; } catch (final IOException e) { throw new ServerAlreadyBootingException(); } } + public static Boolean requiresJNI() { + final boolean isMac = System.getProperty("os.name").toLowerCase().startsWith("mac"); + return isMac && !System.getProperty("os.arch", "").equals("x86_64"); + } + private static String socketName(String sock) { return isWindows ? "\\\\.\\pipe\\" + sock : sock; } diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 90ead4fc8..664f92013 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -83,6 +83,12 @@ object BasicKeys { "Configures the security level of the named pipe. Values: 0 - No security; 1 - Logon user only; 2 - Process owner only", 10000 ) + val serverUseJni = + AttributeKey[Boolean]( + "serverUseJni", + "Toggles whether to use the jna or jni implementation in ipcsocket.", + 10000 + ) val serverIdleTimeout = AttributeKey[Option[FiniteDuration]]( diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index ccf33bbe4..9e47c279e 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -1228,7 +1228,8 @@ object NetworkClient { val err = new PrintStream(term.errorStream) val out = if (redirectOutput) err else new PrintStream(term.outputStream) val args = parseArgs(arguments.toArray).withBaseDirectory(configuration.baseDirectory) - val client = simpleClient(args, term.inputStream, out, err, useJNI = false) + val useJNI = BootServerSocket.requiresJNI || System.getProperty("sbt.ipcsocket.jni", "false") == "true" + val client = simpleClient(args, term.inputStream, out, err, useJNI = useJNI) clientImpl(client, args.bsp) } private class AccessDeniedException extends Throwable 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 69cc4813b..96c970f40 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -66,12 +66,13 @@ private[sbt] object Server { addServerError( new Win32NamedPipeServerSocket( pipeName, - false, + connection.useJni, connection.windowsServerSecurityLevel ) ) case ConnectionType.Local => - val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1 + val maxSocketLength = + UnixDomainSocketLibraryProvider.maxSocketLength(connection.useJni) - 1 val path = socketfile.getAbsolutePath if (path.length > maxSocketLength) sys.error( @@ -80,9 +81,9 @@ private[sbt] object Server { "or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " + s"Current path: ${path}" ) - tryClient(new UnixDomainSocket(path)) + tryClient(new UnixDomainSocket(path, connection.useJni)) prepareSocketfile() - addServerError(new UnixDomainServerSocket(path)) + addServerError(new UnixDomainServerSocket(path, connection.useJni)) case ConnectionType.Tcp => tryClient(new Socket(InetAddress.getByName(host), port)) addServerError(new ServerSocket(port, 50, InetAddress.getByName(host))) @@ -238,7 +239,8 @@ private[sbt] case class ServerConnection( socketfile: File, pipeName: String, appConfiguration: AppConfiguration, - windowsServerSecurityLevel: Int + windowsServerSecurityLevel: Int, + useJni: Boolean, ) { def shortName: String = { connectionType match { diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 3090859b3..6bd2b83c9 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -383,6 +383,7 @@ object Defaults extends BuildCommon { }, serverHandlers :== Nil, windowsServerSecurityLevel := Win32SecurityLevel.OWNER_DACL, // allows any owner logon session to access the server + serverUseJni := BootServerSocket.requiresJNI || SysProp.serverUseJni, fullServerHandlers := Nil, insideCI :== sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI") || SysProp.ci, diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 3e45c51b3..c54d7d1f4 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -105,6 +105,7 @@ object Keys { val serverConnectionType = SettingKey(BasicKeys.serverConnectionType) val serverIdleTimeout = SettingKey(BasicKeys.serverIdleTimeout) val windowsServerSecurityLevel = SettingKey(BasicKeys.windowsServerSecurityLevel) + val serverUseJni = SettingKey(BasicKeys.serverUseJni) val fullServerHandlers = SettingKey(BasicKeys.fullServerHandlers) val serverHandlers = settingKey[Seq[ServerHandler]]("User-defined server handlers.") diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 61c8cc222..99e37e963 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -130,6 +130,7 @@ private[sbt] object xMain { case _: ServerAlreadyBootingException => if (SysProp.forceServerStart) (None, None) else (None, Some(Exit(2))) + case _: UnsatisfiedLinkError => (None, None) } } diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 232410149..dce88bc66 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -27,6 +27,7 @@ import Keys.{ serverIdleTimeout, serverLog, serverPort, + serverUseJni, serverAuthentication, serverConnectionType, fullServerHandlers, @@ -529,10 +530,12 @@ object Project extends ProjectExtra { projectCommand ) val winSecurityLevel = get(windowsServerSecurityLevel).getOrElse(2) + val useJni = get(serverUseJni).getOrElse(false) val newAttrs = s.attributes .put(historyPath.key, history) .put(windowsServerSecurityLevel.key, winSecurityLevel) + .put(serverUseJni.key, useJni) .setCond(autoStartServer.key, startSvr) .setCond(serverPort.key, port) .setCond(serverHost.key, host) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 3569287bb..10ebee08b 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -189,6 +189,7 @@ private[sbt] final class CommandExchange { lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp) lazy val handlers = s.get(fullServerHandlers).getOrElse(Nil) lazy val win32Level = s.get(windowsServerSecurityLevel).getOrElse(2) + lazy val useJni = s.get(serverUseJni).getOrElse(false) lazy val portfile = s.baseDir / "project" / "target" / "active.json" def onIncomingSocket(socket: Socket, instance: ServerInstance): Unit = { @@ -223,6 +224,7 @@ private[sbt] final class CommandExchange { pipeName, s.configuration, win32Level, + useJni, ) val serverInstance = Server.start(connection, onIncomingSocket, s.log) // don't throw exception when it times out diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index 86bf9a0b6..c761e7f04 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -178,6 +178,8 @@ object SysProp { } } + def serverUseJni = getOrFalse("sbt.ipcsocket.jni") + private[this] def file(value: String): File = new File(value) private[this] def home: File = file(sys.props("user.home")) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a1018a978..683110f69 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,7 +12,7 @@ object Dependencies { sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version") // sbt modules - private val ioVersion = nightlyVersion.getOrElse("1.4.0") + private val ioVersion = nightlyVersion.getOrElse("1.4.0") // revert the swoval library dependency in build.sbt mainProj when this is next bumped private val lmVersion = sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.4.1") val zincVersion = nightlyVersion.getOrElse("1.4.3") @@ -26,7 +26,7 @@ object Dependencies { val launcherInterface = "org.scala-sbt" % "launcher-interface" % launcherVersion val rawLauncher = "org.scala-sbt" % "launcher" % launcherVersion val testInterface = "org.scala-sbt" % "test-interface" % "1.0" - val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.1.0" + val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.3.0" private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion