diff --git a/pom.xml b/pom.xml
index d1a2753..14abc6f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,6 @@
2.0.1
12.4.0
- C:\Program Files\Java\jdk-17
@@ -214,9 +213,6 @@
17
true
- true
- ${jdk17.home}\bin\javac
- 17
@@ -234,36 +230,6 @@
-
- com.akathist.maven.plugins.launch4j
- launch4j-maven-plugin
- 2.5.3
-
-
- l4j-exe
- package
- launch4j
-
- gui
- ${project.build.directory}/${project.build.finalName}.jar
- ${project.build.directory}/workutils.exe
- ${project.basedir}/src/main/resources/app_icon.ico
- false
-
- org.springframework.boot.loader.JarLauncher
-
-
- jre
- 17
-
- -Dfile.encoding=UTF-8
-
-
-
-
-
-
-
org.apache.maven.plugins
@@ -280,7 +246,7 @@
${basedir}/scripts
- start.bat
+ start.sh
@@ -294,15 +260,62 @@
- jdk17-home-env
+ windows-package
-
- env.JDK17_HOME
-
+ Windows
-
- ${env.JDK17_HOME}
-
+
+
+
+ com.akathist.maven.plugins.launch4j
+ launch4j-maven-plugin
+ 2.5.3
+
+
+ l4j-exe
+ package
+ launch4j
+
+ gui
+ ${project.build.directory}/${project.build.finalName}.jar
+ ${project.build.directory}/workutils.exe
+ ${project.basedir}/src/main/resources/app_icon.ico
+ false
+
+ org.springframework.boot.loader.JarLauncher
+
+
+ jre
+ 17
+ -Dfile.encoding=UTF-8
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.2.0
+
+
+ copy-windows-start-script
+ package
+ copy-resources
+
+ ${project.build.directory}
+
+
+ ${basedir}/scripts
+ start.bat
+
+
+
+
+
+
+
+
diff --git a/scripts/start.bat b/scripts/start.bat
index ca31d95..ba2b895 100644
--- a/scripts/start.bat
+++ b/scripts/start.bat
@@ -52,5 +52,5 @@ echo Java: %JAVA_VER% ^( %JAVA_EXE% ^)
echo ====================================
echo.
-"%JAVA_EXE%" -Dfile.encoding=UTF-8 -jar "%JAR%" %*
+"%JAVA_EXE%" -Dfile.encoding=UTF-8 "-Dworkutils.home=%CD%" -jar "%JAR%" %*
if %errorlevel% neq 0 pause
diff --git a/src/test/java/com/example/workutils/system/support/BuildPortabilityTest.java b/src/test/java/com/example/workutils/system/support/BuildPortabilityTest.java
new file mode 100644
index 0000000..1443860
--- /dev/null
+++ b/src/test/java/com/example/workutils/system/support/BuildPortabilityTest.java
@@ -0,0 +1,109 @@
+package com.example.workutils.system.support;
+
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class BuildPortabilityTest {
+
+ private final Path projectRoot = Path.of("").toAbsolutePath().normalize();
+
+ @Test
+ void compilerUsesTheMavenJdkWithoutAnExternalExecutable() throws Exception {
+ Document pom = parsePom();
+ Element compiler = pluginUnder(directChild(element(pom, "project"), "build"),
+ "maven-compiler-plugin");
+
+ assertEquals("17", childText(directChild(compiler, "configuration"), "release"));
+ assertFalse(hasDirectChild(directChild(compiler, "configuration"), "executable"));
+ assertFalse(hasDirectChild(directChild(compiler, "configuration"), "fork"));
+ }
+
+ @Test
+ void launch4jOnlyBelongsToTheWindowsProfile() throws Exception {
+ Document pom = parsePom();
+ Element project = element(pom, "project");
+ assertFalse(hasPlugin(directChild(project, "build"), "launch4j-maven-plugin"));
+
+ Element windowsProfile = profile(project, "windows-package");
+ assertEquals("Windows", childText(directChild(directChild(windowsProfile, "activation"), "os"), "family"));
+ assertTrue(hasPlugin(directChild(windowsProfile, "build"), "launch4j-maven-plugin"));
+ }
+
+ @Test
+ void portableShellLauncherIsShippedByTheDefaultBuild() throws Exception {
+ assertTrue(Files.isRegularFile(projectRoot.resolve(Path.of("scripts", "start.sh"))));
+ Document pom = parsePom();
+ Element resourcesPlugin = pluginUnder(
+ directChild(element(parsePom(), "project"), "build"), "maven-resources-plugin");
+ assertTrue(resourcesPlugin.getTextContent().contains("start.sh"));
+ assertFalse(resourcesPlugin.getTextContent().contains("start.bat"));
+ }
+
+ private Document parsePom() throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(false);
+ return factory.newDocumentBuilder().parse(projectRoot.resolve("pom.xml").toFile());
+ }
+
+ private static Element element(Document document, String name) {
+ return (Element) document.getElementsByTagName(name).item(0);
+ }
+
+ private static Element directChild(Element parent, String name) {
+ for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
+ if (node instanceof Element child && name.equals(child.getTagName())) return child;
+ }
+ throw new AssertionError("Missing element " + name + " under " + parent.getTagName());
+ }
+
+ private static boolean hasDirectChild(Element parent, String name) {
+ for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
+ if (node instanceof Element child && name.equals(child.getTagName())) return true;
+ }
+ return false;
+ }
+
+ private static String childText(Element parent, String name) {
+ return directChild(parent, name).getTextContent().trim();
+ }
+
+ private static Element pluginUnder(Element build, String artifactId) {
+ Element plugins = directChild(build, "plugins");
+ NodeList nodes = plugins.getElementsByTagName("plugin");
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Element plugin = (Element) nodes.item(i);
+ if (artifactId.equals(childText(plugin, "artifactId"))) return plugin;
+ }
+ throw new AssertionError("Missing plugin " + artifactId);
+ }
+
+ private static boolean hasPlugin(Element build, String artifactId) {
+ try {
+ pluginUnder(build, artifactId);
+ return true;
+ } catch (AssertionError ignored) {
+ return false;
+ }
+ }
+
+ private static Element profile(Element project, String id) {
+ Element profiles = directChild(project, "profiles");
+ NodeList nodes = profiles.getElementsByTagName("profile");
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Element profile = (Element) nodes.item(i);
+ if (id.equals(childText(profile, "id"))) return profile;
+ }
+ throw new AssertionError("Missing profile " + id);
+ }
+}