mirror of
https://github.com/ij-plugins/ijp-imagej-launcher.git
synced 2024-11-13 16:29:01 -08:00
Determine total system memory
This commit is contained in:
parent
ab28e2daf7
commit
4d793fcc27
8 changed files with 130 additions and 127 deletions
|
|
@ -11,4 +11,6 @@ docstrings.wrap = no
|
|||
importSelectors = singleLine
|
||||
newlines.source = keep
|
||||
|
||||
rewrite.scala3.convertToNewSyntax = yes
|
||||
rewrite.scala3.convertToNewSyntax = yes
|
||||
rewrite.scala3.removeOptionalBraces = yes
|
||||
rewrite.scala3.insertEndMarkerMinLines = 42
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ libraryDependencies ++= Seq(
|
|||
Compile / run / mainClass := Some("ij_plugins.imagej_launcher.Main")
|
||||
|
||||
// import to add Scala Native options
|
||||
import scala.scalanative.build.*
|
||||
import scala.scalanative.build._
|
||||
|
||||
// defaults set with common options shown
|
||||
nativeConfig ~= { c =>
|
||||
|
|
|
|||
19
src/main/resources/scala-native/mem.c
Normal file
19
src/main/resources/scala-native/mem.c
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
unsigned long long determineTotalSystemMemory()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
GlobalMemoryStatusEx(&status);
|
||||
return status.ullTotalPhys;
|
||||
#else
|
||||
long pages = sysconf(_SC_PHYS_PAGES);
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
return pages * page_size;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved.
|
||||
* Author's e-mail: jpsacha at gmail.com
|
||||
*/
|
||||
package ij_plugins.imagej_launcher
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
enum ErrorCode(val value: Int, val message: String):
|
||||
case OK extends ErrorCode(0, "OK")
|
||||
case InvalidCommandLineArguments extends ErrorCode(-10, "Invalid command line arguments")
|
||||
case GeneralError extends ErrorCode(-100, "General error")
|
||||
case UnhandledNonFatalError extends ErrorCode(-200, "Unhandled non-fatal error")
|
||||
case UnhandledFatalError extends ErrorCode(-300, "Unhandled fatal error")
|
||||
case NotImplemented extends ErrorCode(-400, "Functionality not implemented")
|
||||
|
||||
override def toString: String = s"$message [exit code: $value]"
|
||||
|
|
@ -9,37 +9,34 @@ import ij_plugins.imagej_launcher.Launcher.javaExeFileName
|
|||
import ij_plugins.imagej_launcher.Main.Config
|
||||
import os.Path
|
||||
|
||||
import java.io.{BufferedReader, File, InputStreamReader}
|
||||
import java.io.File
|
||||
import java.lang.ProcessBuilder.Redirect
|
||||
import java.util.Locale
|
||||
|
||||
class Launcher(logger: Logger) {
|
||||
class Launcher(logger: Logger):
|
||||
|
||||
private val jarsDirName = "jars"
|
||||
|
||||
def run(config: Config): ErrorCode = {
|
||||
def run(config: Config): Unit =
|
||||
createCommandLine(config) match
|
||||
case Right(commandLine) =>
|
||||
if config.dryRun then
|
||||
logger.debug("dry-run")
|
||||
println(commandLine.mkString(" "))
|
||||
ErrorCode.OK
|
||||
else
|
||||
launch(commandLine)
|
||||
case Left(errorMessage) =>
|
||||
logger.error(errorMessage)
|
||||
ErrorCode.GeneralError
|
||||
}
|
||||
|
||||
private def createCommandLine(config: Config): Either[String, Seq[String]] =
|
||||
for {
|
||||
for
|
||||
ijDir <- locateIJDir(config)
|
||||
launcherJar <- findImageJLauncherJar(ijDir)
|
||||
javaExe <- locateJavaExecutable(config, ijDir)
|
||||
systemType <- determineSystemType()
|
||||
} yield {
|
||||
buildCommandLine(ijDir, javaExe, launcherJar, systemType)
|
||||
}
|
||||
yield
|
||||
val maxMemoryMB = determineMaxMemoryMB()
|
||||
logger.info(s"Max memory to use: ${maxMemoryMB}MB")
|
||||
buildCommandLine(ijDir, javaExe, launcherJar, systemType, maxMemoryMB)
|
||||
|
||||
private def locateIJDir(config: Config): Either[String, File] =
|
||||
logger.debug("Looking for ImageJ directory")
|
||||
|
|
@ -54,22 +51,32 @@ class Launcher(logger: Logger) {
|
|||
|
||||
dir.listFiles().find(f => f.getName == jarsDirName && f.isDirectory) match
|
||||
case Some(_) =>
|
||||
logger.debug(s" ImageJ directory set to: $dir")
|
||||
logger.info(s" ImageJ directory set to: '$dir'")
|
||||
Right(dir)
|
||||
case None =>
|
||||
Left(s"Cannot locate ImageJ directory. No subdirectory '$jarsDirName' in '$dir''")
|
||||
|
||||
private def launch(command: Seq[String]): ErrorCode =
|
||||
logger.debug("launchImageJ ...")
|
||||
|
||||
logger.debug(s"Executing command:\n" + command.mkString(" "))
|
||||
|
||||
val builder = new ProcessBuilder(command*)
|
||||
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT)
|
||||
builder.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
|
||||
val process = builder.start()
|
||||
ErrorCode.OK
|
||||
private def findImageJLauncherJar(ijDir: File): Either[String, File] =
|
||||
logger.debug("Looking for 'imagej-launcher*.jar'")
|
||||
val jarsDir = new File(ijDir, jarsDirName)
|
||||
if jarsDir.exists() then
|
||||
if jarsDir.isDirectory then
|
||||
// Locate launcher
|
||||
jarsDir
|
||||
.listFiles()
|
||||
.find { f =>
|
||||
val name = f.getName
|
||||
name.startsWith("imagej-launcher") && name.endsWith(".jar")
|
||||
} match
|
||||
case Some(f) =>
|
||||
logger.info(s" Found launcher jar: '$f'")
|
||||
Right(f)
|
||||
case None =>
|
||||
Left(s"Cannot find 'imagej-launcher*.jar' in '$jarsDir'")
|
||||
else
|
||||
Left(s"'$jarsDir' is not a directory'")
|
||||
else
|
||||
Left(s"Cannot find subdirectory '$jarsDirName' [$jarsDir]")
|
||||
|
||||
private def locateJavaExecutable(config: Config, ijDir: File): Either[String, File] =
|
||||
logger.debug("Looking for Java executable")
|
||||
|
|
@ -92,11 +99,11 @@ class Launcher(logger: Logger) {
|
|||
Left("Unable to determine Java home")
|
||||
|
||||
javaHomeE.flatMap(javaHome =>
|
||||
logger.debug(s" Java home set as: $javaHome")
|
||||
logger.info(s" Java home set to: '$javaHome'")
|
||||
logger.debug(s" Looking for java executable bin/$javaExeFileName")
|
||||
val javaExe = new File(javaHome, s"bin/$javaExeFileName")
|
||||
if javaExe.exists() then
|
||||
logger.debug(s" Found '$javaExeFileName' in '$javaHome/bin'")
|
||||
logger.info(s" Found '$javaExeFileName' in: '$javaHome/bin'")
|
||||
Right(javaExe)
|
||||
else
|
||||
Left("cannot find 'java.exe' in 'path'")
|
||||
|
|
@ -117,51 +124,44 @@ class Launcher(logger: Logger) {
|
|||
else
|
||||
None
|
||||
|
||||
private def findImageJLauncherJar(ijDir: File): Either[String, File] =
|
||||
logger.debug("Looking for 'imagej-launcher*.jar'")
|
||||
val jarsDir = new File(ijDir, jarsDirName)
|
||||
if jarsDir.exists() then
|
||||
if jarsDir.isDirectory then
|
||||
// Locate launcher
|
||||
jarsDir
|
||||
.listFiles()
|
||||
.find { f =>
|
||||
val name = f.getName
|
||||
name.startsWith("imagej-launcher") && name.endsWith(".jar")
|
||||
} match
|
||||
case Some(f) =>
|
||||
logger.debug(s"Found: '$f'")
|
||||
Right(f)
|
||||
case None =>
|
||||
Left(s"Cannot find 'imagej-launcher*.jar' in '$jarsDir'")
|
||||
else
|
||||
Left(s"'$jarsDir' is not a directory'")
|
||||
else
|
||||
Left(s"Cannot find subdirectory '$jarsDirName' [$jarsDir]")
|
||||
|
||||
private def determineSystemType(): Either[String, String] = {
|
||||
private def determineSystemType(): Either[String, String] =
|
||||
logger.debug("Determining system type")
|
||||
val osName = System.getProperty("os.name")
|
||||
val osArch = System.getProperty("os.arch")
|
||||
logger.debug("os.name: " + osName)
|
||||
logger.debug("os.arch: " + osArch)
|
||||
logger.debug(" os.name: " + osName)
|
||||
logger.debug(" os.arch: " + osArch)
|
||||
|
||||
val notSupportedError = s"$osName $osArch not supported"
|
||||
|
||||
if osName.toLowerCase.startsWith("windows") then
|
||||
if osArch.toLowerCase.contains("64") then
|
||||
Right("win64")
|
||||
val r =
|
||||
if osName.toLowerCase.startsWith("windows") then
|
||||
if osArch.toLowerCase.contains("64") then
|
||||
Right("win64")
|
||||
else
|
||||
Left(notSupportedError)
|
||||
else if osName.toLowerCase.contains("mac os x") then
|
||||
Right("macosx")
|
||||
else
|
||||
Left(notSupportedError)
|
||||
else if osName.toLowerCase.contains("mac os x") then
|
||||
if osArch.toLowerCase.contains("aarch64") then
|
||||
Right("aarch64")
|
||||
else
|
||||
Left(notSupportedError)
|
||||
else
|
||||
Left(notSupportedError)
|
||||
}
|
||||
|
||||
private def buildCommandLine(ijDir: File, javaExe: File, launcherJar: File, systemType: String): Seq[String] = {
|
||||
r.foreach(s => logger.info(s" System type set to: '$s'"))
|
||||
r
|
||||
|
||||
private def determineMaxMemoryMB(): Long =
|
||||
logger.debug("Determine memory to use")
|
||||
val sysMax = Native.mem.determineTotalSystemMemory()
|
||||
logger.debug(s" Available RAM: ${sysMax / 1024 / 1024}MB")
|
||||
val ijMaxMemMB = Math.round(sysMax * 0.75 / 1024 / 1024)
|
||||
logger.debug(s" Using 3/4 of that: ${ijMaxMemMB}MB")
|
||||
ijMaxMemMB
|
||||
|
||||
private def buildCommandLine(
|
||||
ijDir: File,
|
||||
javaExe: File,
|
||||
launcherJar: File,
|
||||
systemType: String,
|
||||
maxMemoryMB: Long
|
||||
): Seq[String] =
|
||||
val ijDirPath = ijDir.getAbsolutePath
|
||||
|
||||
Seq(
|
||||
|
|
@ -172,9 +172,11 @@ class Launcher(logger: Logger) {
|
|||
"java.base/java.util=ALL-UNNAMED",
|
||||
"--add-opens",
|
||||
"java.desktop/sun.awt=ALL-UNNAMED",
|
||||
"--add-opens",
|
||||
"java.desktop/com.apple.eawt=ALL-UNNAMED",
|
||||
"-Dpython.cachedir.skip=true",
|
||||
s"-Dplugins.dir=$ijDirPath",
|
||||
"-Xmx2048m",
|
||||
s"-Xmx${maxMemoryMB}m",
|
||||
"-Dimagej.splash=true",
|
||||
s"-Djava.class.path=${launcherJar.getAbsolutePath}",
|
||||
s"-Dimagej.dir=$ijDirPath",
|
||||
|
|
@ -192,8 +194,17 @@ class Launcher(logger: Logger) {
|
|||
"plugins",
|
||||
"net.imagej.Main"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def launch(command: Seq[String]): Unit =
|
||||
logger.debug("launchImageJ ...")
|
||||
logger.debug(s" Executing command:\n" + command.mkString(" "))
|
||||
|
||||
val builder = new ProcessBuilder(command*)
|
||||
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT)
|
||||
builder.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
|
||||
builder.start()
|
||||
end Launcher
|
||||
|
||||
object Launcher:
|
||||
lazy val javaExeFileName: String = if Utils.isWindows then "java.exe" else "java"
|
||||
|
|
|
|||
|
|
@ -7,22 +7,22 @@ package ij_plugins.imagej_launcher
|
|||
|
||||
import ij_plugins.imagej_launcher.Logger.Level
|
||||
|
||||
class Logger {
|
||||
class Logger:
|
||||
|
||||
var level: Level = Level.Info
|
||||
|
||||
def debug(msg: String): Unit = pprint(Level.Debug, msg)
|
||||
def info(msg: String): Unit = pprint(Level.Info, msg)
|
||||
def error(msg: String): Unit = pprint(Level.Error, msg)
|
||||
|
||||
private def pprint(l: Level, message: String): Unit =
|
||||
if l.level <= level.level then println(s"${l.name}: $message")
|
||||
}
|
||||
|
||||
def error(msg: String): Unit = pprint(Level.Error, msg)
|
||||
|
||||
object Logger:
|
||||
enum Level(val level: Int, val name: String):
|
||||
case Off extends Level(0, "OFF")
|
||||
case Off extends Level(0, "OFF")
|
||||
case Error extends Level(200, "ERROR")
|
||||
case Info extends Level(400, "INFO")
|
||||
case Info extends Level(400, "INFO")
|
||||
case Debug extends Level(500, "DEBUG")
|
||||
case All extends Level(Int.MaxValue, "DEBUG")
|
||||
case All extends Level(Int.MaxValue, "DEBUG")
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@
|
|||
|
||||
package ij_plugins.imagej_launcher
|
||||
|
||||
import ij_plugins.imagej_launcher.ErrorCode
|
||||
import scopt.{DefaultOEffectSetup, OParser}
|
||||
|
||||
import java.io.File
|
||||
|
||||
object Main {
|
||||
object Main:
|
||||
private val logger = new Logger()
|
||||
private val AppName = "ijp-imagej-launcher"
|
||||
// private val AppVersion = s"${Version.version} [${Version.buildTimeStr}]"
|
||||
|
|
@ -20,44 +19,18 @@ object Main {
|
|||
"""Native launcher for ImageJ2
|
||||
|""".stripMargin
|
||||
|
||||
case class Config(
|
||||
logLevel: Logger.Level = Logger.Level.Error,
|
||||
dryRun: Boolean = false,
|
||||
javaHome: Option[File] = None,
|
||||
ijDir: Option[File] = None
|
||||
)
|
||||
|
||||
def main(args: Array[String]): Unit =
|
||||
setupLogger(Logger.Level.All)
|
||||
|
||||
printInfo()
|
||||
|
||||
val ret: ErrorCode =
|
||||
parseCommandLine(args) match
|
||||
case Some(config) =>
|
||||
setupLogger(config.logLevel)
|
||||
runLauncher(config)
|
||||
case None =>
|
||||
// arguments are bad
|
||||
ErrorCode.InvalidCommandLineArguments
|
||||
|
||||
val msg = s"${ret.message} [exit code: ${ret.value}]"
|
||||
if ret == ErrorCode.OK then
|
||||
logger.info(msg)
|
||||
else
|
||||
logger.error(msg)
|
||||
|
||||
// System.exit(ret.value)
|
||||
end main
|
||||
|
||||
private def printInfo(): Unit =
|
||||
logger.debug("os.arch: " + System.getProperty("os.arch"))
|
||||
logger.debug("os.name: " + System.getProperty("os.name"))
|
||||
// logger.debug("os.version: " + System.getProperty("os.version"))
|
||||
parseCommandLine(args) match
|
||||
case Some(config) =>
|
||||
setupLogger(config.logLevel)
|
||||
runLauncher(config)
|
||||
case None =>
|
||||
|
||||
private def parseCommandLine(args: Array[String]): Option[Config] =
|
||||
val builder = OParser.builder[Config]
|
||||
val parser1 = {
|
||||
val parser1 =
|
||||
import builder.*
|
||||
OParser.sequence(
|
||||
programName(AppName),
|
||||
|
|
@ -94,12 +67,18 @@ object Main {
|
|||
.action((path, c) => c.copy(ijDir = Option(path)))
|
||||
.text("set the ImageJ directory to <path> (used to find jars/, plugins/ and macros/)")
|
||||
)
|
||||
}
|
||||
|
||||
OParser.parse(parser1, args, Config())
|
||||
end parseCommandLine
|
||||
|
||||
private def setupLogger(logLevel: Logger.Level): Unit = logger.level = logLevel
|
||||
|
||||
private def runLauncher(config: Config): ErrorCode = new Launcher(logger).run(config)
|
||||
}
|
||||
private def runLauncher(config: Config): Unit = new Launcher(logger).run(config)
|
||||
|
||||
case class Config(
|
||||
logLevel: Logger.Level = Logger.Level.Error,
|
||||
dryRun: Boolean = false,
|
||||
javaHome: Option[File] = None,
|
||||
ijDir: Option[File] = None
|
||||
)
|
||||
end Main
|
||||
|
|
|
|||
9
src/main/scala/ij_plugins/imagej_launcher/Native.scala
Normal file
9
src/main/scala/ij_plugins/imagej_launcher/Native.scala
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package ij_plugins.imagej_launcher
|
||||
|
||||
import scala.scalanative.unsafe.extern
|
||||
|
||||
object Native:
|
||||
|
||||
@extern
|
||||
object mem:
|
||||
def determineTotalSystemMemory(): Long = extern
|
||||
Loading…
Add table
Add a link
Reference in a new issue