mirror of
https://github.com/ij-plugins/ijp-imagej-launcher.git
synced 2024-11-13 16:29:01 -08:00
First version that can launch Fiji natively on Windows and Mac
This commit is contained in:
parent
d694d564fd
commit
ab28e2daf7
5 changed files with 221 additions and 7 deletions
|
|
@ -5,10 +5,195 @@
|
|||
|
||||
package ij_plugins.imagej_launcher
|
||||
|
||||
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.lang.ProcessBuilder.Redirect
|
||||
import java.util.Locale
|
||||
|
||||
class Launcher(logger: Logger) {
|
||||
|
||||
private val jarsDirName = "jars"
|
||||
|
||||
def run(config: Config): ErrorCode = {
|
||||
ErrorCode.NotImplemented
|
||||
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 {
|
||||
ijDir <- locateIJDir(config)
|
||||
launcherJar <- findImageJLauncherJar(ijDir)
|
||||
javaExe <- locateJavaExecutable(config, ijDir)
|
||||
systemType <- determineSystemType()
|
||||
} yield {
|
||||
buildCommandLine(ijDir, javaExe, launcherJar, systemType)
|
||||
}
|
||||
|
||||
private def locateIJDir(config: Config): Either[String, File] =
|
||||
logger.debug("Looking for ImageJ directory")
|
||||
|
||||
val dir = config.ijDir match
|
||||
case Some(d) =>
|
||||
logger.debug(" Considering provided ij-dir")
|
||||
d
|
||||
case None =>
|
||||
logger.debug(" Considering current directory")
|
||||
new File(".").getCanonicalFile
|
||||
|
||||
dir.listFiles().find(f => f.getName == jarsDirName && f.isDirectory) match
|
||||
case Some(_) =>
|
||||
logger.debug(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 locateJavaExecutable(config: Config, ijDir: File): Either[String, File] =
|
||||
logger.debug("Looking for Java executable")
|
||||
|
||||
val javaHomeE: Either[String, File] = config.javaHome match
|
||||
case Some(d) =>
|
||||
logger.debug(s" Setting java-home to provided: '$d'")
|
||||
Right(d)
|
||||
case None =>
|
||||
locateJavaHomeIn(new File(ijDir, "java")) match
|
||||
case Some(d) =>
|
||||
logger.debug(s" Located java dir in ImageJ home")
|
||||
Right(d)
|
||||
case None =>
|
||||
Option(System.getenv("JAVA_HOME")) match
|
||||
case Some(d) =>
|
||||
logger.debug(" Using JAVA_HOME environment variable")
|
||||
Right(new File(d).getAbsoluteFile)
|
||||
case None =>
|
||||
Left("Unable to determine Java home")
|
||||
|
||||
javaHomeE.flatMap(javaHome =>
|
||||
logger.debug(s" Java home set as: $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'")
|
||||
Right(javaExe)
|
||||
else
|
||||
Left("cannot find 'java.exe' in 'path'")
|
||||
)
|
||||
|
||||
private def locateJavaHomeIn(dir: File): Option[File] =
|
||||
if dir.exists() && dir.isDirectory then
|
||||
val base = os.Path(dir.toPath)
|
||||
val candidates: Seq[Path] = os.walk(path = base)
|
||||
val c2 = candidates.filter(p =>
|
||||
p.toIO.isFile &&
|
||||
p.toIO.getName == javaExeFileName &&
|
||||
p.toIO.getParentFile.getName == "bin"
|
||||
)
|
||||
logger.debug(" Candidates: " + c2.mkString(", "))
|
||||
c2.map(_.toIO.getParentFile.getParentFile)
|
||||
.headOption
|
||||
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] = {
|
||||
val osName = System.getProperty("os.name")
|
||||
val osArch = System.getProperty("os.arch")
|
||||
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")
|
||||
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] = {
|
||||
val ijDirPath = ijDir.getAbsolutePath
|
||||
|
||||
Seq(
|
||||
javaExe.getAbsolutePath,
|
||||
"--add-opens",
|
||||
"java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens",
|
||||
"java.base/java.util=ALL-UNNAMED",
|
||||
"--add-opens",
|
||||
"java.desktop/sun.awt=ALL-UNNAMED",
|
||||
"-Dpython.cachedir.skip=true",
|
||||
s"-Dplugins.dir=$ijDirPath",
|
||||
"-Xmx2048m",
|
||||
"-Dimagej.splash=true",
|
||||
s"-Djava.class.path=${launcherJar.getAbsolutePath}",
|
||||
s"-Dimagej.dir=$ijDirPath",
|
||||
s"-Dij.dir=$ijDirPath",
|
||||
s"-Dfiji.dir=$ijDirPath",
|
||||
s"-Dfiji.executable=$ijDirPath/ImageJ-$systemType.exe",
|
||||
s"-Dij.executable=$ijDirPath/ImageJ-$systemType.exe",
|
||||
s"-Djava.library.path=$ijDirPath/lib/$systemType;$ijDirPath/mm/$systemType",
|
||||
"-Dscijava.context.strict=false",
|
||||
"-Dpython.console.encoding=UTF-8",
|
||||
"net.imagej.launcher.ClassLauncher",
|
||||
"-ijjarpath",
|
||||
"jars",
|
||||
"-ijjarpath",
|
||||
"plugins",
|
||||
"net.imagej.Main"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Launcher:
|
||||
lazy val javaExeFileName: String = if Utils.isWindows then "java.exe" else "java"
|
||||
|
|
|
|||
|
|
@ -23,12 +23,15 @@ object Main {
|
|||
case class Config(
|
||||
logLevel: Logger.Level = Logger.Level.Error,
|
||||
dryRun: Boolean = false,
|
||||
javaHome: Option[File] = None
|
||||
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) =>
|
||||
|
|
@ -47,6 +50,11 @@ object Main {
|
|||
// 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"))
|
||||
|
||||
private def parseCommandLine(args: Array[String]): Option[Config] =
|
||||
val builder = OParser.builder[Config]
|
||||
val parser1 = {
|
||||
|
|
@ -79,7 +87,12 @@ object Main {
|
|||
//
|
||||
opt[Unit]("print-java-home")
|
||||
.action((_, c) => c.copy(dryRun = true))
|
||||
.text("print ImageJ's idea of JAVA_HOME")
|
||||
.text("print ImageJ's idea of JAVA_HOME"),
|
||||
//
|
||||
opt[File]("ij-dir")
|
||||
.valueName("<path>")
|
||||
.action((path, c) => c.copy(ijDir = Option(path)))
|
||||
.text("set the ImageJ directory to <path> (used to find jars/, plugins/ and macros/)")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
8
src/main/scala/ij_plugins/imagej_launcher/Utils.scala
Normal file
8
src/main/scala/ij_plugins/imagej_launcher/Utils.scala
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package ij_plugins.imagej_launcher
|
||||
|
||||
object Utils:
|
||||
def isWindows: Boolean =
|
||||
System
|
||||
.getProperty("os.name")
|
||||
.toLowerCase()
|
||||
.startsWith("windows")
|
||||
Loading…
Add table
Add a link
Reference in a new issue