From fef142e697ba9599928742fa45810e462899085f Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 25 Nov 2020 07:27:31 -0800 Subject: [PATCH 1/4] Update ipcsocket The new version provides an apple silicon arm64 jni implementation of the ipcsocket api. It also adds a jni implementation for getting the max socket length which is necessary because jna isn't supported on the apple arm64 platform yet. --- main-command/src/main/scala/sbt/internal/server/Server.scala | 3 ++- project/Dependencies.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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..979de1a2f 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -71,7 +71,8 @@ private[sbt] object Server { ) ) 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( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a1018a978..b3d1a9a94 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -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 From 29f0989554917cd9c83a4f5810f883ccb8dc25a5 Mon Sep 17 00:00:00 2001 From: eugene yokota Date: Mon, 23 Nov 2020 16:14:05 -0500 Subject: [PATCH 2/4] Merge pull request #6165 from eatkins/ipcsocket-jna Use JNI implementation of ipcsocket APIs on Arm Macs --- .../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 979de1a2f..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,7 +66,7 @@ private[sbt] object Server { addServerError( new Win32NamedPipeServerSocket( pipeName, - false, + connection.useJni, connection.windowsServerSecurityLevel ) ) @@ -81,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))) @@ -239,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")) From 402067452db86abc6d1095f6717d1405aa8bd374 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 25 Nov 2020 07:39:37 -0800 Subject: [PATCH 3/4] Evict io version of swoval file tree views This version of swoval supports the apple arm64 platform. --- build.sbt | 1 + project/Dependencies.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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/project/Dependencies.scala b/project/Dependencies.scala index b3d1a9a94..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") From bc8048ced487dfa2091e10cd97bf6addb33cccc6 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 24 Nov 2020 12:32:15 -0800 Subject: [PATCH 4/4] Use jni if required when running client from xMain See https://github.com/sbt/sbt/issues/6162#issuecomment-733207878. --- .../src/main/scala/sbt/internal/client/NetworkClient.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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