From ab2875e83723b750c3fc0269e80a231442e99d4f Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Mon, 23 Nov 2020 10:17:47 -0800 Subject: [PATCH] Use jni implementation of ipcsocket apis on arm macs There isn't yet a version of the jna available that works with the new apple silicon using arm64. To workaround this, we can use the jni implementation by default on arm64 macs. If the user wants to force the jni implementation for any supported platform, they can opt in with the `sbt.ipcsocket.jni` system property and/or by setting the serverUseJni setting. --- .../src/main/java/sbt/internal/BootServerSocket.java | 11 ++++++++--- main-command/src/main/scala/sbt/BasicKeys.scala | 6 ++++++ .../src/main/scala/sbt/internal/server/Server.scala | 9 +++++---- main/src/main/scala/sbt/Defaults.scala | 1 + main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/Main.scala | 1 + main/src/main/scala/sbt/Project.scala | 3 +++ .../src/main/scala/sbt/internal/CommandExchange.scala | 2 ++ main/src/main/scala/sbt/internal/SysProp.scala | 2 ++ 9 files changed, 29 insertions(+), 7 deletions(-) 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"))