mirror of
https://github.com/ij-plugins/ijp-imagej-launcher.git
synced 2024-11-13 16:29:01 -08:00
Compare commits
No commits in common. "master" and "v.0.1.0" have entirely different histories.
23 changed files with 109 additions and 489 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,4 +2,3 @@
|
||||||
.idea/
|
.idea/
|
||||||
target/
|
target/
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
version = 3.8.3
|
version = 3.7.4
|
||||||
|
|
||||||
runner.dialect = scala3
|
runner.dialect = scala3
|
||||||
|
|
||||||
|
|
|
||||||
96
ReadMe.md
96
ReadMe.md
|
|
@ -15,8 +15,6 @@ IJP ImageJ Launcher is a clean implementation on the core function of starting [
|
||||||
* [Installing Fiji on Mac OS X Arm64](#installing-fiji-on-mac-os-x-arm64)
|
* [Installing Fiji on Mac OS X Arm64](#installing-fiji-on-mac-os-x-arm64)
|
||||||
* [Installing Fiji on Windows x64](#installing-fiji-on-windows-x64)
|
* [Installing Fiji on Windows x64](#installing-fiji-on-windows-x64)
|
||||||
* [Troubleshooting](#troubleshooting)
|
* [Troubleshooting](#troubleshooting)
|
||||||
* [Start-up log `~/.ijp_imagej_launcher.log`](#start-up-log-ijpimagejlauncherlog)
|
|
||||||
* [Starting from command prompt](#starting-from-command-prompt)
|
|
||||||
* [Developer Setup](#developer-setup)
|
* [Developer Setup](#developer-setup)
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
@ -35,10 +33,7 @@ the logic flow is too complex to correct without a significant rewrite.
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Here are the futures that are already implemented (see release notes for futures ofa specific release):
|
* Uses similar options to the original ImageJ Launcher, si IJP Launcher can be drop-in replacement
|
||||||
|
|
||||||
* Uses similar options to the original ImageJ Launcher, so IJP Launcher can be used as a drop-in replacement
|
|
||||||
* Intended to be used with Java 11 or newer (the original launcher can be used for Java 8)
|
|
||||||
* Provides native executable for various OS/Hardware systems
|
* Provides native executable for various OS/Hardware systems
|
||||||
- Windows
|
- Windows
|
||||||
- Mac OS X Arm64 (Apple Silicon)
|
- Mac OS X Arm64 (Apple Silicon)
|
||||||
|
|
@ -53,7 +48,7 @@ Here are the futures that are already implemented (see release notes for futures
|
||||||
- Search ImageJ directory for available Java executables
|
- Search ImageJ directory for available Java executables
|
||||||
* Determines the amount of memory used by JVM based on total system memory use 75% of the max
|
* Determines the amount of memory used by JVM based on total system memory use 75% of the max
|
||||||
* Determines available `imagej-launcher*.jar`
|
* Determines available `imagej-launcher*.jar`
|
||||||
* **Performs updates** pending after the last time ImageJ was closed
|
* Performs updates pending after the last time ImageJ was closed
|
||||||
|
|
||||||
Full List of Command Line Options
|
Full List of Command Line Options
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
@ -82,7 +77,7 @@ This example will show how to:
|
||||||
|
|
||||||
**1. Download FIJI without JRE**
|
**1. Download FIJI without JRE**
|
||||||
|
|
||||||
Go to https://imagej.net/software/fiji/downloads and download the **"No JRE"** version (not specific to any OS).
|
Go to https://fiji.sc/ and select "Download the no-JRE version".
|
||||||
That should get file called `fiji-nojre.zip`
|
That should get file called `fiji-nojre.zip`
|
||||||
|
|
||||||
**2. Unzip the `fiji-nojre.zip` in a folder of choice**
|
**2. Unzip the `fiji-nojre.zip` in a folder of choice**
|
||||||
|
|
@ -100,49 +95,41 @@ Inside the `Fiji.app` folder create a new folder called `java`.
|
||||||
In browser open https://adoptium.net/temurin/releases/
|
In browser open https://adoptium.net/temurin/releases/
|
||||||
Select:
|
Select:
|
||||||
|
|
||||||
* Operating System: `macOS`
|
* operating system: `macOS`
|
||||||
* Architecture: `aarch64` also known as Apple Silicon or Arm64
|
* architecture: `aarch64` also known as Apple Silicon or Arm64
|
||||||
* Package Type: `JRE` (`JDK` is fine too, is larger and supports Java compilation)
|
* package: `JRE` (`JDK` is fine too, is larger and supports Java compilation)
|
||||||
* Version: `11-LTS` (`17-LTS` will work too, but you will not have JavaScript available, if you want to use it)
|
* version: `11-LTS` (`17-LTS` will work too, but you will not have JavaScript available, if you want to use it)
|
||||||
|
|
||||||
Click on `tar.gz` button to download and save to the `java` folder you created earlier.
|
Click on `tar.gz` button to download and save to the `java` folder you created earlier.
|
||||||
You should have file like `OpenJDK11U-jre_aarch64_mac_hotspot_11.0.20_8.tar.gz`.
|
You should have file like `OpenJDK11U-jre_x64_windows_hotspot_11.0.19_7.tar.gz`.
|
||||||
|
|
||||||
**5. Uncompress into the `Fiji.app/java` folder**
|
**5. Uncompress into the `java` folder**
|
||||||
|
|
||||||
That will create folder like `jdk-11.0.20+8-jre`.
|
That will create folder like `jdk-11.0.19+7-jre`.
|
||||||
This is the Java VM that IJP ImageJ Launcher will use to start Fiji.
|
This is the Java VM that IJP ImageJ Launcher will use to start Fiji.
|
||||||
|
|
||||||
**6. Download the IJP ImageJ Launcher and uncompress**
|
**6. Download the IJP ImageJ Launcher to the Fiji.app directory**
|
||||||
|
|
||||||
Go to [Releases], download "IJP-ImageJ-Launcher-0.2.0-macosx-arm64.zip"
|
Go to [Releases], download "IJP-ImageJ-Launcher-0.1.0-macosx-arm64"
|
||||||
|
and "IJP-ImageJ-Launcher-0.1.0-macosx-arm64.command", save them to the `Fiji.app` folder.
|
||||||
|
|
||||||
Uncompress "IJP-ImageJ-Launcher-0.2.0-macosx-arm64.zip".
|
The "*.command" file is a helper that can be used to launch Fiji without using command prompt.
|
||||||
Inside you will get `ImageJ-macosx`.
|
Future versions of the IJP Launcher, after v.0.1.0, may eliminate the need for using this file.
|
||||||
|
|
||||||
**7. Add to Fiji.app**
|
**7. Start ImageJ**
|
||||||
|
|
||||||
Inside `Fiji.app` locate folder `Contents/MacOS`.
|
In the `Fiji.app` folder double-click on `IJP-ImageL-Launcher-0.1.0-macosx-arm64.command` file (note the extension "*
|
||||||
|
.command")
|
||||||
|
That should start Fiji.
|
||||||
|
You may need to open Settings and allow the IJP ImageJ Launcher to run.
|
||||||
|
|
||||||
Copy `ImageJ-macosx` to the `Contents/MacOS` folder, replacing `ImageJ-macosx` that was there.
|
You can also create an alis on the Desktop to avoid navigating to the `Fiji.app` folder each time.
|
||||||
|
Using Finder, press `Option`+`Command` and drag the *.command file to the Desktop.
|
||||||
**8. Move Fiji.app to the Application folder**
|
The original *.command file will stay were it is and a new icon/alias (wth a little arrow at the bottom) will be created
|
||||||
|
on the Desktop.
|
||||||
At this point you can move the `Fiji.app` folder to the Applications folder and use is as a regular msOS application.
|
Now you can double-click on the new alias on the Desktop to start Fiji.
|
||||||
|
You can rename the Desktop alias to whatever you like, for instance, `Fiji`, but do not change names of the downloaded
|
||||||
**9. Troubleshooting**
|
files, otherwise the alias (and *.command) may no longer work, and you will need to use terminal to start the launcher.
|
||||||
|
|
||||||
When you attempt to run Fiji with the new Launcher you may get a warning dialog
|
|
||||||

|
|
||||||
|
|
||||||
Possible work-around
|
|
||||||
|
|
||||||
1. Delete `Fuji.app` folder
|
|
||||||
2. Uncompressed `fiji-nojre.zip` to recreate `Fuji.app` folder, but do not make any changes to it yet. You may need to do it is different folder than before.
|
|
||||||
3. Control-clock on `Fuji.app` and select "Open". You will see dialog saying
|
|
||||||
"macOS cannot verify the developer of “Fiji”. Are you sure you want to open it?"
|
|
||||||
4. Click on "Open". You will see Fiji logo, but the application will close since it is not setup yet
|
|
||||||
5. Now you can repeat steps "3. Create place for Java (JRE)" to "7. Add to Fiji.app" above
|
|
||||||
|
|
||||||
If you have problems installing, please report in [Discussions] or [Image.sc Forum]
|
If you have problems installing, please report in [Discussions] or [Image.sc Forum]
|
||||||
|
|
||||||
|
|
@ -156,7 +143,7 @@ This example will show how to:
|
||||||
|
|
||||||
**1. Download FIJI without JRE**
|
**1. Download FIJI without JRE**
|
||||||
|
|
||||||
Go to https://imagej.net/software/fiji/downloads and download the **"No JRE"** version (not specific to any OS).
|
Go to https://fiji.sc/ and select "Download the no-JRE version".
|
||||||
That should get file called `fiji-nojre.zip`
|
That should get file called `fiji-nojre.zip`
|
||||||
|
|
||||||
**2. Unzip the `fiji-nojre.zip` in a folder of choice**
|
**2. Unzip the `fiji-nojre.zip` in a folder of choice**
|
||||||
|
|
@ -173,31 +160,31 @@ Inside the `Fiji.app` folder create a new folder called `java`.
|
||||||
In browser open https://adoptium.net/temurin/releases/
|
In browser open https://adoptium.net/temurin/releases/
|
||||||
Select:
|
Select:
|
||||||
|
|
||||||
* Operating System: `Windows`
|
* operating system: `Windows`
|
||||||
* Architecture: `x64` also known as Apple Silicon or Arm64
|
* architecture: `x64` also known as Apple Silicon or Arm64
|
||||||
* Package Type: `JRE` (`JDK` is fine too, is larger and supports Java compilation)
|
* package: `JRE` (`JDK` is fine too, is larger and supports Java compilation)
|
||||||
* Version: `11-LTS` (`17-LTS` will work too, but you will not have JavaScript available, if you want to use it)
|
* version: `11-LTS` (`17-LTS` will work too, but you will not have JavaScript available, if you want to use it)
|
||||||
|
|
||||||
Click on `.zip` button to download and save to the `java` folder you created earlier.
|
Click on `.zip` button to download and save to the `java` folder you created earlier.
|
||||||
You should have file like `OpenJDK11U-jre_x64_windows_hotspot_11.0.20_8.zip`.
|
You should have file like `OpenJDK11U-jre_x64_windows_hotspot_11.0.19_7.zip`.
|
||||||
|
|
||||||
**5. Uncompress into the `Fiji.app/java` folder**
|
**5. Uncompress into the `java` folder**
|
||||||
|
|
||||||
That will create folder like `jdk-11.0.20+8-jre`.
|
That will create folder like `jdk-11.0.19+7-jre`.
|
||||||
This is the Java VM that IJP ImageJ Launcher will use to start Fiji.
|
This is the Java VM that IJP ImageJ Launcher will use to start Fiji.
|
||||||
|
|
||||||
**6. Download the IJP ImageJ Launcher to the Fiji.app directory**
|
**6. Download the IJP ImageJ Launcher to the Fiji.app directory**
|
||||||
|
|
||||||
Go to [Releases], download "IJP-ImageJ-Launcher-0.2.0-windows_x64.exe", save it to the `Fiji.app` folder.
|
Go to [Releases], download "IJP-ImageJ-Launcher-0.1.0-windows_x64.exe", save it to the `Fiji.app` folder.
|
||||||
|
|
||||||
**7. Start ImageJ**
|
**7. Start ImageJ**
|
||||||
|
|
||||||
In the `Fiji.app` folder double-click on `IJP-ImageJ-Launcher-0.2.0-windows_x64.exe`.
|
In the `Fiji.app` folder double-click on `IJP-ImageJ-Launcher-0.1.0-windows_x64.exe`.
|
||||||
That should start Fiji.
|
That should start Fiji.
|
||||||
|
|
||||||
You can also create a shortcut on the Desktop to avoid navigating to the `Fiji.app` folder each time.
|
You can also create a shortcut on the Desktop to avoid navigating to the `Fiji.app` folder each time.
|
||||||
|
|
||||||
**_Left_**-click on the `IJP-ImageJ-Launcher-0.2.0-windows_x64.exe` and drag it to the Desktop.
|
**_Left_**-click on the `IJP-ImageJ-Launcher-0.1.0-windows_x64.exe` and drag it to the Desktop.
|
||||||
Once you release mouse button, a pop-up manu will open, select "Create shortcut here".
|
Once you release mouse button, a pop-up manu will open, select "Create shortcut here".
|
||||||
Now you can double-click on the new shortcut on the Desktop to start Fiji.
|
Now you can double-click on the new shortcut on the Desktop to start Fiji.
|
||||||
|
|
||||||
|
|
@ -207,13 +194,6 @@ If you have problems installing, please report in [Discussions] or [Image.sc For
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
#### Start-up log `~/.ijp_imagej_launcher.log`
|
|
||||||
|
|
||||||
The IJP-ImageJ-Launcher writes diagnostic info to a file `.ijp_imagej_launcher.log` in the users home directory.
|
|
||||||
The information recorded is some as using `--debug` on command line.
|
|
||||||
|
|
||||||
#### Starting from command prompt
|
|
||||||
|
|
||||||
You can start the IJP Image Launcher from the terminal and see diagnostic printouts that may help troubleshoot potential
|
You can start the IJP Image Launcher from the terminal and see diagnostic printouts that may help troubleshoot potential
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 215 KiB |
27
build.sbt
27
build.sbt
|
|
@ -1,6 +1,6 @@
|
||||||
scalaVersion := "3.3.3"
|
scalaVersion := "3.3.0"
|
||||||
//name := "IJP-ImageJ-Launcher"
|
//name := "IJP-ImageJ-Launcher"
|
||||||
version := "0.2.0.1-SNAPSHOT"
|
version := "0.1.0"
|
||||||
versionScheme := Some("early-semver")
|
versionScheme := Some("early-semver")
|
||||||
organization := "net.sf.ij-plugins"
|
organization := "net.sf.ij-plugins"
|
||||||
homepage := Some(new URI("https://github.com/ij-plugins/ijp-imagej-launcher").toURL)
|
homepage := Some(new URI("https://github.com/ij-plugins/ijp-imagej-launcher").toURL)
|
||||||
|
|
@ -16,26 +16,14 @@ enablePlugins(ScalaNativePlugin)
|
||||||
logLevel := Level.Info
|
logLevel := Level.Info
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"com.github.scopt" %%% "scopt" % "4.1.0",
|
"com.github.scopt" %%% "scopt" % "4.1.0",
|
||||||
"com.lihaoyi" %%% "os-lib" % "0.9.3",
|
"com.lihaoyi" %%% "os-lib" % "0.9.1"
|
||||||
"org.scalatest" %%% "scalatest" % "3.2.18" % Test
|
|
||||||
)
|
|
||||||
|
|
||||||
scalacOptions ++= Seq(
|
|
||||||
"-unchecked",
|
|
||||||
"-deprecation",
|
|
||||||
"-explain",
|
|
||||||
"-explain-types",
|
|
||||||
"-rewrite",
|
|
||||||
"-source:3.3-migration",
|
|
||||||
// "-Wvalue-discard",
|
|
||||||
"-Wunused:all"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / run / mainClass := Some("ij_plugins.imagej_launcher.Main")
|
Compile / run / mainClass := Some("ij_plugins.imagej_launcher.Main")
|
||||||
|
|
||||||
// import to add Scala Native options
|
// import to add Scala Native options
|
||||||
import scala.scalanative.build.*
|
import scala.scalanative.build._
|
||||||
|
|
||||||
// defaults set with common options shown
|
// defaults set with common options shown
|
||||||
nativeConfig ~= { c =>
|
nativeConfig ~= { c =>
|
||||||
|
|
@ -48,8 +36,3 @@ nativeConfig ~= { c =>
|
||||||
//nativeConfig ~= { c =>
|
//nativeConfig ~= { c =>
|
||||||
// c.withCompileOptions(c.compileOptions ++ Seq("-v"))
|
// c.withCompileOptions(c.compileOptions ++ Seq("-v"))
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// Version info generation from SBT configuration
|
|
||||||
enablePlugins(BuildInfoPlugin)
|
|
||||||
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion)
|
|
||||||
buildInfoPackage := "ij_plugins.imagej_launcher"
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
DIR=$(cd "$(dirname "$0")" && pwd -P)
|
DIR=$(cd "$(dirname "$0")" && pwd -P)
|
||||||
echo "$DIR"
|
echo "$DIR"
|
||||||
"$DIR"/IJP-ImageJ-Launcher-0.1.0-macosx-arm64 --debug --ij-dir "$DIR"
|
"$DIR"/IJP-ImageL-Launcher-0.1.0-macosx-arm64 --debug --ij-dir "$DIR"
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
Feature release: support better system integration on macOS - support installing ImageJ/Fiji in the application
|
|
||||||
directory.
|
|
||||||
See macOS installation info in the [ReadMe](https://github.com/ij-plugins/ijp-imagej-launcher)
|
|
||||||
|
|
||||||
* Use launcher location to infer `ij-dir`. #5
|
|
||||||
* Session log is saved to `~/.ijp_imagej_launcher.log` to facilitate troubleshooting when not running from command
|
|
||||||
prompt. The log is reset for each session. #6
|
|
||||||
* Better inference of ImageJ directory on macOS - consider launcher being in subdirectory "Contents/MacOS" #7
|
|
||||||
|
|
@ -1,6 +1 @@
|
||||||
#
|
sbt.version = 1.8.3
|
||||||
# Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved.
|
|
||||||
# Author's e-mail: jpsacha at gmail.com
|
|
||||||
#
|
|
||||||
|
|
||||||
sbt.version = 1.10.2
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
// https://github.com/sbt/sbt-buildinfo
|
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0")
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
// https://github.com/scala-native/scala-native
|
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.12")
|
||||||
resolvers ++= Resolver.sonatypeOssRepos("snapshots")
|
|
||||||
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17")
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/syslimits.h>
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t path_max() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return MAX_PATH;
|
|
||||||
#else
|
|
||||||
return PATH_MAX + 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_exe_path(char* exe_path, size_t size) {
|
|
||||||
|
|
||||||
exe_path[0] = 0;
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
// char arg1[20];
|
|
||||||
// sprintf( arg1, "/proc/%d/exe", getpid() );
|
|
||||||
// printf("arg1 = %d\n", arg1);
|
|
||||||
// readlink( arg1, exepath, PATH_MAX );
|
|
||||||
readlink( "/proc/self/exe", exe_path, size );
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
if (_NSGetExecutablePath(exe_path, &size) != 0)
|
|
||||||
printf("ERROR: buffer too small; need size %lu\n", size);
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
unsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exe_path, size);
|
|
||||||
if (len == 0) // memory not sufficient or general error occurred
|
|
||||||
printf("ERROR: buffer too small or general error.\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +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 os.Path
|
|
||||||
|
|
||||||
object IJConfigFile:
|
|
||||||
|
|
||||||
private val FileName: String = "ImageJ.cfg"
|
|
||||||
private val MagicHeader: String = "# ImageJ startup properties"
|
|
||||||
private val MaxHeapMBKey: String = "maxheap.mb"
|
|
||||||
private val JvmArgsKey: String = "jvmargs"
|
|
||||||
private val LegacyModeKey: String = "legacy.mode"
|
|
||||||
private val AssignmentSymbol: String = "="
|
|
||||||
private val CommentSymbol: String = "#"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read `ImageJ.cfg` from a specified directory.
|
|
||||||
* The configuration file must be in ImageJ2 format (with magic header "# ImageJ startup properties").
|
|
||||||
*
|
|
||||||
* @param dir directory where `ImageJ.cfg` is located
|
|
||||||
* @param logger logger
|
|
||||||
* @return option containing the configuration file (Right).
|
|
||||||
* The option is empty if file is not present in input `dir`.
|
|
||||||
* An error message is returned if file is present but cannot be read without errors (Left).
|
|
||||||
*/
|
|
||||||
def readFromDir(dir: Path)(using logger: Logger): Either[String, Option[IJCConfig]] =
|
|
||||||
logger.debug(s"Reading ImageJ configuration from directory: $dir")
|
|
||||||
val path = dir / FileName
|
|
||||||
logger.debug(s" Looking for $FileName in: '$path'")
|
|
||||||
readFromFile(path)
|
|
||||||
|
|
||||||
private[imagej_launcher] def readFromFile(ijConfigFile: Path)(using logger: Logger): Either[String, Option[IJCConfig]] =
|
|
||||||
if os.exists(ijConfigFile) then
|
|
||||||
val lines = os.read.lines(ijConfigFile)
|
|
||||||
val cfgE =
|
|
||||||
lines.headOption match
|
|
||||||
case Some(line) =>
|
|
||||||
if line.trim.startsWith(MagicHeader) then
|
|
||||||
logger.debug(s" Parsing $FileName")
|
|
||||||
for
|
|
||||||
props <- parseProps(lines)
|
|
||||||
maxHeapMB <- parseLong(props, MaxHeapMBKey)
|
|
||||||
jvmArgs <- parseString(props, JvmArgsKey)
|
|
||||||
legacyMode <- parseBoolean(props, LegacyModeKey)
|
|
||||||
yield Option(IJCConfig(maxHeapMB, jvmArgs, legacyMode))
|
|
||||||
else
|
|
||||||
Left(s"$FileName is invalid, does not contain header: '$MagicHeader'")
|
|
||||||
case None =>
|
|
||||||
Left(s"$FileName is empty.")
|
|
||||||
|
|
||||||
// Add context to the error message, if there is one
|
|
||||||
cfgE.left.map(e => s"Failed to read $FileName. $e")
|
|
||||||
else
|
|
||||||
logger.debug(s" $FileName not found: $ijConfigFile")
|
|
||||||
Right(None)
|
|
||||||
|
|
||||||
private[imagej_launcher] def parseProps(lines: Seq[String]): Either[String, Map[String, String]] =
|
|
||||||
// Parse each line
|
|
||||||
val lineResults: Seq[Either[String, (String, String)]] =
|
|
||||||
lines
|
|
||||||
.zipWithIndex
|
|
||||||
.filter { case (line, _) => !line.startsWith(CommentSymbol) }
|
|
||||||
.map { case (line, lineNumber) =>
|
|
||||||
val index = line.indexOf(AssignmentSymbol)
|
|
||||||
if index > 0 then
|
|
||||||
val key = line.substring(0, index).trim
|
|
||||||
if !key.isBlank then
|
|
||||||
Right(key -> line.substring(index + 1).trim)
|
|
||||||
else
|
|
||||||
Left(s"Error in line $lineNumber: key cannot be empty: '$line'")
|
|
||||||
else if index == 0 then
|
|
||||||
Left(s"Error in line $lineNumber: line cannot start with an assignment symbol '$AssignmentSymbol': '$line'")
|
|
||||||
else
|
|
||||||
Left(s"Error in line $lineNumber: no assignment symbol '$AssignmentSymbol' present: '$line'")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate lines with errors
|
|
||||||
val errors = lineResults.collect { case e: Left[?, ?] => e }
|
|
||||||
if errors.isEmpty then
|
|
||||||
// Convert key->value pairs to a map
|
|
||||||
val r =
|
|
||||||
lineResults
|
|
||||||
.collect { case e: Right[?, ?] => e }
|
|
||||||
.map(_.value)
|
|
||||||
.toMap
|
|
||||||
Right(r)
|
|
||||||
else
|
|
||||||
Left(errors.map(_.value).mkString("\n"))
|
|
||||||
|
|
||||||
private def parseLong(props: Map[String, String], key: String): Either[String, Long] =
|
|
||||||
parseString(props, key).flatMap: s =>
|
|
||||||
s.toLongOption match
|
|
||||||
case Some(v) => Right(v)
|
|
||||||
case None => Left(s"Failed to parse '$s' as an integer.")
|
|
||||||
|
|
||||||
private def parseString(props: Map[String, String], key: String): Either[String, String] =
|
|
||||||
props.get(key) match
|
|
||||||
case Some(v) => Right(v)
|
|
||||||
case None => Left(s"No key named '$key'.")
|
|
||||||
|
|
||||||
private def parseBoolean(props: Map[String, String], key: String): Either[String, Boolean] =
|
|
||||||
parseString(props, key).flatMap: s =>
|
|
||||||
s.toBooleanOption match
|
|
||||||
case Some(v) => Right(v)
|
|
||||||
case None => Left(s"Failed to parse '$s' as a boolean.")
|
|
||||||
|
|
||||||
/** Configuration loaded from `ImageJ.cfg`. */
|
|
||||||
case class IJCConfig(maxHeapMB: Long, jvmArgs: String, legacyMode: Boolean)
|
|
||||||
|
|
||||||
end IJConfigFile
|
|
||||||
|
|
@ -1,73 +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 ij_plugins.imagej_launcher.Main.Config
|
|
||||||
import os.{FilePath, Path}
|
|
||||||
|
|
||||||
import scala.util.control.NonFatal
|
|
||||||
|
|
||||||
object IJDir:
|
|
||||||
|
|
||||||
/** Name of an ImageJ sub-directory containing jars */
|
|
||||||
val jarsDirName = "jars"
|
|
||||||
|
|
||||||
/** Locate ImageJ directory */
|
|
||||||
def locate(config: Config)(using logger: Logger): Either[String, Path] =
|
|
||||||
logger.debug("Looking for ImageJ directory")
|
|
||||||
|
|
||||||
config.ijDir match
|
|
||||||
case Some(d) =>
|
|
||||||
logger.debug(s" Considering provided ij-dir: '$d'")
|
|
||||||
asPath(d.getPath).flatMap: p =>
|
|
||||||
logger.debug(s" '$p'")
|
|
||||||
if isIJDir(p) then Right(p)
|
|
||||||
else Left(s"ij-dir is not an ImageJ directory [$p]")
|
|
||||||
case None =>
|
|
||||||
logger.debug(" Considering application directory")
|
|
||||||
val appPath = Native.applicationPath()
|
|
||||||
logger.debug(s" Application directory: '$appPath'")
|
|
||||||
inferIJDir(appPath) match
|
|
||||||
case Some(p) =>
|
|
||||||
Right(p)
|
|
||||||
case None =>
|
|
||||||
logger.debug(" Application directory is not an ImageJ directory")
|
|
||||||
logger.debug(" Considering current working directory")
|
|
||||||
val cwd = os.pwd
|
|
||||||
logger.debug(s" Current working directory: '$cwd'")
|
|
||||||
inferIJDir(cwd) match
|
|
||||||
case Some(p) =>
|
|
||||||
Right(p)
|
|
||||||
case None =>
|
|
||||||
logger.debug(" Current working directory is not an ImageJ directory.")
|
|
||||||
Left("Cannot locate ImageJ directory.")
|
|
||||||
|
|
||||||
private def inferIJDir(path: Path): Option[Path] =
|
|
||||||
if isIJDir(path) then
|
|
||||||
Option(path)
|
|
||||||
else if path.endsWith(os.rel / "Contents" / "MacOS") then
|
|
||||||
Option(path / os.up / os.up)
|
|
||||||
else
|
|
||||||
None
|
|
||||||
|
|
||||||
private def isIJDir(path: Path): Boolean =
|
|
||||||
os.exists(path) &&
|
|
||||||
os.isDir(path) &&
|
|
||||||
os.list(path).exists(f => f.last == jarsDirName && os.isDir(f))
|
|
||||||
|
|
||||||
private def asPath(filePath: String): Either[String, Path] =
|
|
||||||
if filePath.trim.startsWith("~") then
|
|
||||||
try
|
|
||||||
Right(Path.expandUser(filePath))
|
|
||||||
catch
|
|
||||||
case NonFatal(ex) =>
|
|
||||||
Left(s"Not an absolute path: '$filePath' [${ex.getMessage}]")
|
|
||||||
else
|
|
||||||
FilePath(filePath) match
|
|
||||||
case p: Path => Right(p)
|
|
||||||
case _ => Left(s"Not an absolute path: '$filePath'")
|
|
||||||
|
|
||||||
end IJDir
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
package ij_plugins.imagej_launcher
|
package ij_plugins.imagej_launcher
|
||||||
|
|
||||||
import ij_plugins.imagej_launcher.IJDir.jarsDirName
|
|
||||||
import ij_plugins.imagej_launcher.Launcher.javaExeFileName
|
import ij_plugins.imagej_launcher.Launcher.javaExeFileName
|
||||||
import ij_plugins.imagej_launcher.Main.Config
|
import ij_plugins.imagej_launcher.Main.Config
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
@ -13,7 +12,9 @@ import os.Path
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ProcessBuilder.Redirect
|
import java.lang.ProcessBuilder.Redirect
|
||||||
|
|
||||||
class Launcher(using logger: Logger):
|
class Launcher(logger: Logger):
|
||||||
|
|
||||||
|
private val jarsDirName = "jars"
|
||||||
|
|
||||||
def run(config: Config): Unit =
|
def run(config: Config): Unit =
|
||||||
prepareLaunch(config) match
|
prepareLaunch(config) match
|
||||||
|
|
@ -28,16 +29,33 @@ class Launcher(using logger: Logger):
|
||||||
|
|
||||||
private def prepareLaunch(config: Config): Either[String, Seq[String]] =
|
private def prepareLaunch(config: Config): Either[String, Seq[String]] =
|
||||||
for
|
for
|
||||||
ijDir <- IJDir.locate(config)
|
ijDir <- locateIJDir(config)
|
||||||
_ <- Updater.update(ijDir, config.dryRun)
|
_ <- Updater.update(Path(ijDir), config.dryRun, logger)
|
||||||
ijConfig <- IJConfigFile.readFromDir(ijDir)
|
launcherJar <- findImageJLauncherJar(ijDir)
|
||||||
launcherJar <- findImageJLauncherJar(ijDir.toIO)
|
javaExe <- locateJavaExecutable(config, ijDir)
|
||||||
javaExe <- locateJavaExecutable(config, ijDir.toIO)
|
|
||||||
systemType <- determineSystemType()
|
systemType <- determineSystemType()
|
||||||
yield
|
yield
|
||||||
val maxMemoryMB = determineMaxMemoryMB()
|
val maxMemoryMB = determineMaxMemoryMB()
|
||||||
logger.info(s"Max memory to use: ${maxMemoryMB}MB")
|
logger.info(s"Max memory to use: ${maxMemoryMB}MB")
|
||||||
buildCommandLine(ijDir.toIO, javaExe, launcherJar, systemType, maxMemoryMB)
|
buildCommandLine(ijDir, javaExe, launcherJar, systemType, maxMemoryMB)
|
||||||
|
|
||||||
|
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.info(s" ImageJ directory set to: '$dir'")
|
||||||
|
Right(dir)
|
||||||
|
case None =>
|
||||||
|
Left(s"Cannot locate ImageJ directory. No subdirectory '$jarsDirName' in '$dir''")
|
||||||
|
|
||||||
private def findImageJLauncherJar(ijDir: File): Either[String, File] =
|
private def findImageJLauncherJar(ijDir: File): Either[String, File] =
|
||||||
logger.debug("Looking for 'imagej-launcher*.jar'")
|
logger.debug("Looking for 'imagej-launcher*.jar'")
|
||||||
|
|
@ -101,7 +119,7 @@ class Launcher(using logger: Logger):
|
||||||
p.toIO.getName == javaExeFileName &&
|
p.toIO.getName == javaExeFileName &&
|
||||||
p.toIO.getParentFile.getName == "bin"
|
p.toIO.getParentFile.getName == "bin"
|
||||||
)
|
)
|
||||||
logger.debug(" Candidates: " + c2.mkString(", "))
|
logger.debug(" Candidates: " + c2.mkString(", "))
|
||||||
c2.map(_.toIO.getParentFile.getParentFile)
|
c2.map(_.toIO.getParentFile.getParentFile)
|
||||||
.headOption
|
.headOption
|
||||||
else
|
else
|
||||||
|
|
@ -179,7 +197,6 @@ class Launcher(using logger: Logger):
|
||||||
"plugins",
|
"plugins",
|
||||||
"net.imagej.Main"
|
"net.imagej.Main"
|
||||||
)
|
)
|
||||||
end buildCommandLine
|
|
||||||
|
|
||||||
private def launch(command: Seq[String]): Unit =
|
private def launch(command: Seq[String]): Unit =
|
||||||
logger.debug("launchImageJ ...")
|
logger.debug("launchImageJ ...")
|
||||||
|
|
|
||||||
|
|
@ -5,60 +5,20 @@
|
||||||
|
|
||||||
package ij_plugins.imagej_launcher
|
package ij_plugins.imagej_launcher
|
||||||
|
|
||||||
import ij_plugins.imagej_launcher.Logger.{Level, logToFile}
|
import ij_plugins.imagej_launcher.Logger.Level
|
||||||
import os.Path
|
|
||||||
|
|
||||||
import java.nio.file.{Files, StandardOpenOption}
|
|
||||||
import scala.util.control.NonFatal
|
|
||||||
|
|
||||||
class Logger(val level: Level = Level.Info):
|
class Logger(val level: Level = Level.Info):
|
||||||
|
|
||||||
def debug(msg: String): Unit = pprint(Level.Debug, msg)
|
def debug(msg: String): Unit = pprint(Level.Debug, msg)
|
||||||
def info(msg: String): Unit = pprint(Level.Info, msg)
|
def info(msg: String): Unit = pprint(Level.Info, msg)
|
||||||
def error(msg: String): Unit = pprint(Level.Error, msg)
|
def error(msg: String): Unit = pprint(Level.Error, msg)
|
||||||
|
|
||||||
private def pprint(l: Level, message: String): Unit =
|
private def pprint(l: Level, message: String): Unit =
|
||||||
val m = f"${l.name}%-5s: $message"
|
|
||||||
if l.level <= level.level then println(f"${l.name}%-5s: $message")
|
if l.level <= level.level then println(f"${l.name}%-5s: $message")
|
||||||
logToFile(m)
|
|
||||||
|
|
||||||
object Logger:
|
object Logger:
|
||||||
|
|
||||||
private val LogFile: Path = os.home / ".ijp_imagej_launcher.log"
|
|
||||||
private var logFileReset: Boolean = true
|
|
||||||
|
|
||||||
enum Level(val level: Int, val name: String):
|
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 Error extends Level(200, "ERROR")
|
||||||
case Info extends Level(400, "INFO")
|
case Info extends Level(400, "INFO")
|
||||||
case Debug extends Level(500, "DEBUG")
|
case Debug extends Level(500, "DEBUG")
|
||||||
case All extends Level(Int.MaxValue, "DEBUG")
|
case All extends Level(Int.MaxValue, "DEBUG")
|
||||||
|
|
||||||
private def logToFile(message: String): Unit =
|
|
||||||
try
|
|
||||||
if logFileReset && os.exists(LogFile) then
|
|
||||||
val _ = os.remove(LogFile)
|
|
||||||
logFileReset = false
|
|
||||||
|
|
||||||
val data = readableTimeStamp() + ": " + message + "\n"
|
|
||||||
val lines = data.getBytes()
|
|
||||||
val _ = Files.write(
|
|
||||||
LogFile.toNIO,
|
|
||||||
lines,
|
|
||||||
StandardOpenOption.CREATE,
|
|
||||||
StandardOpenOption.APPEND,
|
|
||||||
StandardOpenOption.WRITE
|
|
||||||
)
|
|
||||||
catch
|
|
||||||
case NonFatal(_) =>
|
|
||||||
// Ignore exceptions
|
|
||||||
|
|
||||||
private def readableTimeStamp(): String =
|
|
||||||
// Simple implementation returns GMT time (no date)
|
|
||||||
// Use custom code as DateTimeFormatter is not available in Scala Native 0.4.14
|
|
||||||
val t = System.currentTimeMillis()
|
|
||||||
val sss = t % 1000
|
|
||||||
val ss = (t / 1000L).toInt % 60
|
|
||||||
val mm = (t / (1000L * 60L)).toInt % 60
|
|
||||||
val hh = (t / (1000L * 60L * 60L)).toInt % 24
|
|
||||||
f"$hh%02d-$mm%02d-$ss%02d_$sss%03dZ"
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,15 @@
|
||||||
|
|
||||||
package ij_plugins.imagej_launcher
|
package ij_plugins.imagej_launcher
|
||||||
|
|
||||||
import scopt.OParser
|
import scopt.{DefaultOEffectSetup, OParser}
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object Main:
|
object Main:
|
||||||
private var logger = new Logger()
|
private var logger = new Logger()
|
||||||
private val AppName = "IJP-ImageJ-Launcher"
|
private val AppName = "ijp-imagej-launcher"
|
||||||
private val AppVersion = BuildInfo.version
|
// private val AppVersion = s"${Version.version} [${Version.buildTimeStr}]"
|
||||||
|
private val AppVersion = s"0.1.0"
|
||||||
private val VersionMessage = s"v.$AppVersion"
|
private val VersionMessage = s"v.$AppVersion"
|
||||||
private val AppDescription =
|
private val AppDescription =
|
||||||
"""Native launcher for ImageJ2
|
"""Native launcher for ImageJ2
|
||||||
|
|
@ -28,7 +29,6 @@ object Main:
|
||||||
case None =>
|
case None =>
|
||||||
|
|
||||||
private def parseCommandLine(args: Array[String]): Option[Config] =
|
private def parseCommandLine(args: Array[String]): Option[Config] =
|
||||||
logger.debug("Command line: " + args.map("'" + _ + "'").mkString(", "))
|
|
||||||
val builder = OParser.builder[Config]
|
val builder = OParser.builder[Config]
|
||||||
val parser1 =
|
val parser1 =
|
||||||
import builder.*
|
import builder.*
|
||||||
|
|
@ -69,7 +69,7 @@ object Main:
|
||||||
|
|
||||||
private def setupLogger(logLevel: Logger.Level): Unit = logger = new Logger(logLevel)
|
private def setupLogger(logLevel: Logger.Level): Unit = logger = new Logger(logLevel)
|
||||||
|
|
||||||
private def runLauncher(config: Config): Unit = new Launcher(using logger).run(config)
|
private def runLauncher(config: Config): Unit = new Launcher(logger).run(config)
|
||||||
|
|
||||||
case class Config(
|
case class Config(
|
||||||
logLevel: Logger.Level = Logger.Level.Error,
|
logLevel: Logger.Level = Logger.Level.Error,
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,9 @@
|
||||||
package ij_plugins.imagej_launcher
|
package ij_plugins.imagej_launcher
|
||||||
|
|
||||||
import os.{Path, up}
|
import scala.scalanative.unsafe.extern
|
||||||
|
|
||||||
import scala.scalanative.unsafe
|
|
||||||
import scala.scalanative.unsafe.*
|
|
||||||
|
|
||||||
object Native:
|
object Native:
|
||||||
|
|
||||||
def applicationPath(): Path =
|
|
||||||
val maxPath: CSize = argv0.path_max()
|
|
||||||
// use Zone to manage native memory
|
|
||||||
val exePath =
|
|
||||||
Zone { implicit z =>
|
|
||||||
val buffer: CString = alloc[CChar](maxPath)
|
|
||||||
argv0.get_exe_path(buffer, maxPath)
|
|
||||||
unsafe.fromCString(buffer)
|
|
||||||
}
|
|
||||||
Path(exePath) / up
|
|
||||||
|
|
||||||
@extern
|
@extern
|
||||||
object mem:
|
object mem:
|
||||||
def determineTotalSystemMemory(): Long = extern
|
def determineTotalSystemMemory(): Long = extern
|
||||||
|
|
||||||
@extern
|
|
||||||
private object argv0:
|
|
||||||
def path_max(): CSize = extern
|
|
||||||
|
|
||||||
def get_exe_path(exe_path: CString, size: CSize): Unit = extern
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved.
|
|
||||||
* Author's e-mail: jpsacha at gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ij_plugins.imagej_launcher
|
package ij_plugins.imagej_launcher
|
||||||
|
|
||||||
import os.Path
|
import os.{Path, RelPath}
|
||||||
|
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
|
|
@ -18,7 +13,7 @@ object Updater:
|
||||||
* @param logger configured logger
|
* @param logger configured logger
|
||||||
* @return Number of files processed or an error message.
|
* @return Number of files processed or an error message.
|
||||||
*/
|
*/
|
||||||
def update(ijDir: Path, dryRun: Boolean)(using logger: Logger): Either[String, Long] =
|
def update(ijDir: Path, dryRun: Boolean, logger: Logger): Either[String, Long] =
|
||||||
try
|
try
|
||||||
val updateDir = ijDir / "update"
|
val updateDir = ijDir / "update"
|
||||||
// Count used only for debug info
|
// Count used only for debug info
|
||||||
|
|
@ -28,7 +23,8 @@ object Updater:
|
||||||
os.walk(updateDir)
|
os.walk(updateDir)
|
||||||
.filter(os.isFile)
|
.filter(os.isFile)
|
||||||
.foreach: src =>
|
.foreach: src =>
|
||||||
val dst = ijDir / src.relativeTo(updateDir)
|
// val relativeDir = src.relativeTo(updateDir)
|
||||||
|
val dst = ijDir / relativeTo(src, updateDir)
|
||||||
if os.size(src) == 0 then
|
if os.size(src) == 0 then
|
||||||
logger.debug(s"remove: $dst")
|
logger.debug(s"remove: $dst")
|
||||||
if !dryRun then os.remove(dst)
|
if !dryRun then os.remove(dst)
|
||||||
|
|
@ -39,7 +35,7 @@ object Updater:
|
||||||
if !dryRun then os.move(src, dst, replaceExisting = true, createFolders = true)
|
if !dryRun then os.move(src, dst, replaceExisting = true, createFolders = true)
|
||||||
count += 1
|
count += 1
|
||||||
logger.debug(s"Delete update directory: $updateDir")
|
logger.debug(s"Delete update directory: $updateDir")
|
||||||
if !dryRun then deleteEmptyDirs(updateDir)
|
if !dryRun then deleteEmptyDirs(updateDir, logger)
|
||||||
Right(count)
|
Right(count)
|
||||||
else
|
else
|
||||||
logger.info("No update found")
|
logger.info("No update found")
|
||||||
|
|
@ -49,11 +45,30 @@ object Updater:
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
Left(s"Failed to perform update: ${ex.getMessage} - ${ex.getClass.getSimpleName}")
|
Left(s"Failed to perform update: ${ex.getMessage} - ${ex.getClass.getSimpleName}")
|
||||||
|
|
||||||
private def deleteEmptyDirs(dir: Path)(using logger: Logger): Unit =
|
private def relativeTo(src: Path, base: Path): RelPath =
|
||||||
|
// This does what src.relativeTo(base) should do
|
||||||
|
// Problems is in the native code on Windows,
|
||||||
|
// os.Path#relativeTo creates relative path by adding `../` at the beginning of the absolute path,
|
||||||
|
// co you may get `../C:\a\b` which leads to a exception soon after.
|
||||||
|
// The issue is with Scala Native implementation of java.nio.file.Path#relativize on Windows,
|
||||||
|
// See https://github.com/scala-native/scala-native/issues/3293
|
||||||
|
|
||||||
|
// This implementation is very limited but sufficient for our use.
|
||||||
|
// It assumes specific relation between src and base.
|
||||||
|
|
||||||
|
val srcStr = src.toString
|
||||||
|
val baseStr = base.toString
|
||||||
|
require(baseStr.nonEmpty)
|
||||||
|
require(srcStr.startsWith(baseStr))
|
||||||
|
|
||||||
|
val relStr = srcStr.drop(baseStr.length + 1)
|
||||||
|
RelPath(relStr)
|
||||||
|
|
||||||
|
private def deleteEmptyDirs(dir: Path, logger: Logger): Unit =
|
||||||
logger.debug(s"Cleaning directory: $dir")
|
logger.debug(s"Cleaning directory: $dir")
|
||||||
os.list(dir)
|
os.list(dir)
|
||||||
.filter(os.isDir)
|
.filter(os.isDir)
|
||||||
.foreach(p => deleteEmptyDirs(p))
|
.foreach(p => deleteEmptyDirs(p, logger))
|
||||||
|
|
||||||
if os.list(dir).isEmpty then
|
if os.list(dir).isEmpty then
|
||||||
logger.debug(s"Removing empty dir: $dir")
|
logger.debug(s"Removing empty dir: $dir")
|
||||||
|
|
|
||||||
|
|
@ -1,33 +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 org.scalatest.flatspec.AnyFlatSpec
|
|
||||||
|
|
||||||
class IJConfigFileTest extends AnyFlatSpec:
|
|
||||||
|
|
||||||
given logger:Logger = new Logger(Logger.Level.All)
|
|
||||||
|
|
||||||
"IJConfigFile" should "read valid config files" in:
|
|
||||||
val path = os.pwd / "test" / "data" / "config_valid" / "ImageJ.cfg"
|
|
||||||
|
|
||||||
IJConfigFile.readFromFile(path) match
|
|
||||||
case Right(cfg) =>
|
|
||||||
logger.debug(cfg.toString)
|
|
||||||
assert(cfg.nonEmpty)
|
|
||||||
case Left(err) =>
|
|
||||||
logger.debug(s"Unexpected error: $err")
|
|
||||||
fail()
|
|
||||||
|
|
||||||
it should "fail reading invalid config files" in:
|
|
||||||
val path = os.pwd / "test" / "data" / "config_invalid" / "ImageJ.cfg"
|
|
||||||
|
|
||||||
IJConfigFile.readFromFile(path) match
|
|
||||||
case Right(cfg) =>
|
|
||||||
logger.debug(cfg.toString)
|
|
||||||
fail(s"Expected to return errors when reading config, but read config as: $cfg")
|
|
||||||
case Left(err) =>
|
|
||||||
logger.debug(s"Expected error\n:$err")
|
|
||||||
|
|
@ -1,28 +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 org.scalatest.flatspec.AnyFlatSpec
|
|
||||||
import org.scalatest.matchers.should
|
|
||||||
|
|
||||||
import java.nio.file.FileSystems
|
|
||||||
import scala.scalanative.runtime.Platform.isWindows
|
|
||||||
|
|
||||||
class PathSpec extends AnyFlatSpec with should.Matchers:
|
|
||||||
|
|
||||||
"A Path" should "should `relativize` jars on Windows (Scala Native #3293)" in {
|
|
||||||
if isWindows() then
|
|
||||||
// This to test that fix for Scala Native #3293 implemented, it should be part of Scala Native 0.4.13
|
|
||||||
// See https://github.com/scala-native/scala-native/issues/3293
|
|
||||||
|
|
||||||
// val src = Path.of("C:\\a\\b\\c.jar")
|
|
||||||
val src = FileSystems.getDefault.getPath("C:\\a\\b\\c.jar")
|
|
||||||
// val base = Path.of("C:\\a")
|
|
||||||
val base = FileSystems.getDefault.getPath("C:\\a")
|
|
||||||
|
|
||||||
val rel = base.relativize(src)
|
|
||||||
rel.toString should be("b\\c.jar")
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@ import os.Path
|
||||||
|
|
||||||
@main
|
@main
|
||||||
def updaterDemo(ijDir: String): Unit =
|
def updaterDemo(ijDir: String): Unit =
|
||||||
given logger: Logger = new Logger(Logger.Level.Debug)
|
Updater.update(Path(ijDir), dryRun = false, new Logger(Logger.Level.Debug)) match
|
||||||
Updater.update(Path(ijDir), dryRun = false) match
|
|
||||||
case Right(count) => println(s"Processed $count files")
|
case Right(count) => println(s"Processed $count files")
|
||||||
case Left(error) => println(s"Failed with error: $error")
|
case Left(error) => println(s"Failed with error: $error")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# ImageJ startup properties
|
|
||||||
maxheap.mb = 1024m
|
|
||||||
jvmargs = -XX:+HeapDumpOnOutOfMemoryError -Xincgc
|
|
||||||
legacy.mode = false
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# ImageJ startup properties
|
|
||||||
maxheap.mb = 1024
|
|
||||||
jvmargs = -XX:+HeapDumpOnOutOfMemoryError -Xincgc
|
|
||||||
legacy.mode = false
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue