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/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 69cc4813b..5066a27fb 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -66,7 +66,7 @@ private[sbt] object Server { addServerError( new Win32NamedPipeServerSocket( pipeName, - false, + connection.useJni, connection.windowsServerSecurityLevel ) ) @@ -80,9 +80,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 +238,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 95dc58118..e915d65c0 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 49d1e4baf..e379c8108 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"))