Build self-contained Windows JAR release

This commit is contained in:
张家训
2026-08-03 15:37:06 +08:00
parent 516989b8e6
commit f0950429a6
4 changed files with 121 additions and 229 deletions
+1 -1
View File
@@ -262,7 +262,7 @@
<profile>
<id>windows-package</id>
<activation>
<os><family>Windows</family></os>
<property><name>windows.package</name></property>
</activation>
<build>
<plugins>
+105 -235
View File
@@ -1,249 +1,119 @@
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
setlocal EnableExtensions EnableDelayedExpansion
:: ============================================
:: WorkUtils 生产打包脚本(免安装版)
:: 输出:target\dist\workutils-v{version}.zip
:: (含 exe + 内嵌JRE + jar + 启动脚本)
:: ============================================
:: 切换到项目根目录
cd /d "%~dp0.."
set "PROJECT_DIR=%CD%"
set "TARGET=%PROJECT_DIR%\target"
set "DIST=%TARGET%\dist"
set "RELEASE_DIR=%DIST%\workutils"
set "BUILD_JDK="
echo.
echo ========== WorkUtils Windows portable release ==========
echo Project: %PROJECT_DIR%
call :try_jdk "%JDK17_HOME%"
call :try_jdk "%JAVA_HOME%"
call :try_jdk "C:\Program Files\Java\jdk-17"
call :try_jdk "C:\Program Files\Eclipse Adoptium\jdk-17"
if not defined BUILD_JDK (
echo [ERROR] A JDK 17 or newer with jlink.exe is required to build the release.
echo Set JAVA_HOME or JDK17_HOME to a valid JDK directory.
pause
exit /b 1
)
set "JAVA_HOME=%BUILD_JDK%"
set "PATH=%JAVA_HOME%\bin;%PATH%"
echo Build JDK: %JAVA_HOME%
echo.
echo [1/5] Build and test executable JAR...
call "%PROJECT_DIR%\mvnw.cmd" clean package
if errorlevel 1 goto :build_failed
echo.
echo [2/5] Read project version...
set "VERSION="
echo.
echo ========== WorkUtils 生产构建 ==========
echo 项目目录: %PROJECT_DIR%
echo.
:: ============================================================
:: 步骤 1:清理残留进程 & 解锁删除旧产物
:: ============================================================
echo [1/5] Clean stale processes and files...
taskkill /f /im java.exe 2>nul
taskkill /f /im workutils.exe 2>nul
:: 等进程完全退出,文件锁释放
echo 等待进程退出...
ping 127.0.0.1 -n 4 >nul
:: --- 清理 target/dist ---
if exist "%TARGET%" (
:: 策略1:直接 rd(文件锁已释放时应成功)
rd /s /q "%TARGET%" 2>nul
:: 策略2:如果 rd 失败,改名后重建
if exist "%TARGET%" (
echo [WARN] 直接删除失败,尝试改名清理...
:: 先删 dist/ 子目录的文件(规避 JRE 文件锁)
if exist "%DIST%" (
del /f /s /q "%DIST%\*" 2>nul
rd /s /q "%DIST%" 2>nul
)
:: 再删 jre/
if exist "%TARGET%\jre" (
del /f /s /q "%TARGET%\jre\*" 2>nul
rd /s /q "%TARGET%\jre" 2>nul
)
:: 删 target/ 下其余文件
del /f /s /q "%TARGET%\*" 2>nul
for /d %%d in ("%TARGET%\*") do rd /s /q "%%d" 2>nul
rd /s /q "%TARGET%" 2>nul
)
:: 策略3:改名旧目录,让后续步骤创建新的
if exist "%TARGET%" (
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set "TS=%%I"
set "OLD_TARGET=%PROJECT_DIR%\target_old_!TS:~0,14!"
echo [WARN] 无法删除,重命名为 !OLD_TARGET! ...
move "%TARGET%" "!OLD_TARGET!" >nul 2>&1
if exist "!OLD_TARGET!" (
echo [INFO] 旧产物已移至 !OLD_TARGET!,稍后请手动删除
)
)
)
:: ============================================================
:: 步骤 2:Maven 打包
:: ============================================================
echo.
echo [2/5] Maven build...
call "%PROJECT_DIR%\mvnw.cmd" package -DskipTests -q
if %errorlevel% neq 0 (
echo [ERROR] Maven build failed!
pause
exit /b 1
)
:: ============================================================
:: 步骤 3:提取版本号
:: ============================================================
echo.
echo [3/5] Read version from pom.xml...
for /f "usebackq delims=" %%a in (`powershell -NoProfile -NonInteractive -Command "([xml](Get-Content -Encoding UTF8 '%PROJECT_DIR%\pom.xml')).project.version"`) do set "VERSION=%%a"
if "%VERSION%"=="" set "VERSION=0.0.1-SNAPSHOT"
echo 版本: %VERSION%
:: ============================================================
:: 步骤 4:生成精简 JRE(jlink 内嵌 Java 17 + JavaFX)
:: ============================================================
echo.
echo [4/5] Generate bundled JRE...
:: 4.1 查找 JDK 17
if not defined JDK17_HOME (
for %%d in (
"C:\Program Files\Java\jdk-17"
"C:\Program Files\Eclipse Adoptium\jdk-17.0.0.35-hotspot"
"C:\Program Files\Java\jdk-17.0.14"
"%JAVA_HOME%"
) do (
if exist "%%~d\bin\jlink.exe" (
set "JDK17_HOME=%%~d"
goto :jdk17_found
)
)
echo [ERROR] Cannot find JDK 17 with jlink.exe
echo Please set JDK17_HOME environment variable
pause & exit /b 1
)
:jdk17_found
:: 4.2 查找 JavaFX SDK
if not defined JFX_SDK_HOME (
for %%d in (
"C:\Program Files\Java\javafx-sdk-17.0.15"
"C:\Program Files\Java\javafx-sdk-17.0.14"
"C:\Program Files\Java\javafx-sdk-17"
) do (
if exist "%%~d\lib\javafx.base.jar" (
set "JFX_SDK_HOME=%%~d"
goto :jfx_found
)
)
echo [ERROR] Cannot find JavaFX SDK
echo Please set JFX_SDK_HOME environment variable
pause & exit /b 1
)
:jfx_found
echo JDK : %JDK17_HOME%
echo JavaFX: %JFX_SDK_HOME%
:: 4.3 清理旧 JRE
if exist "%TARGET%\jre" rd /s /q "%TARGET%\jre" 2>nul
:: 4.4 执行 jlink
echo Running jlink, please wait...
"%JDK17_HOME%\bin\jlink.exe" ^
--module-path "%JFX_SDK_HOME%\lib;%JDK17_HOME%\jmods" ^
--add-modules java.base,java.desktop,java.logging,java.management,java.naming,java.sql,java.xml,java.xml.crypto,java.instrument,java.security.jgss,java.security.sasl,jdk.unsupported,jdk.management,java.scripting,jdk.crypto.ec,java.net.http,javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.web,javafx.swing ^
--output "%TARGET%\jre" ^
--strip-debug --no-header-files --no-man-pages --compress=2
if %errorlevel% neq 0 (
echo [ERROR] jlink failed!
if not defined VERSION (
echo [ERROR] Unable to read the project version from pom.xml.
pause
exit /b 1
)
:: 4.5 补充 JavaFX 原生 DLL
echo Copy JavaFX native DLLs...
if exist "%JFX_SDK_HOME%\bin\glass.dll" (
xcopy /y /q "%JFX_SDK_HOME%\bin\*.dll" "%TARGET%\jre\bin\" >nul
echo [OK] JavaFX DLLs copied
) else (
echo [WARN] No JavaFX DLLs found in %JFX_SDK_HOME%\bin
)
echo [OK] JRE generated : %TARGET%\jre
:: ============================================================
:: 步骤 5:组装发布目录 & 打包 ZIP
:: ============================================================
echo.
echo [5/5] Assemble distribution...
:: 清理旧发布目录
if exist "%DIST%" (
del /f /s /q "%DIST%\*" 2>nul
for /d %%d in ("%DIST%\*") do rd /s /q "%%d" 2>nul
rd /s /q "%DIST%" 2>nul
)
mkdir "%DIST%\workutils" 2>nul
:: 复制 EXE
if exist "%TARGET%\workutils.exe" (
copy /y "%TARGET%\workutils.exe" "%DIST%\workutils\" >nul
echo [OK] workutils.exe
) else (
echo [WARN] workutils.exe not found!
)
:: 复制 JAR
for %%j in ("%TARGET%\workutils-*.jar") do (
copy /y "%%j" "%DIST%\workutils\" >nul
echo [OK] %%~nxj
)
:: 复制内嵌 JRE
if exist "%TARGET%\jre\bin\java.exe" (
echo 复制内嵌 JRE...
xcopy /e /i /q "%TARGET%\jre" "%DIST%\workutils\jre\" >nul
echo [OK] jre/ ^(bundled Java 17 + JavaFX^)
) else (
echo [WARN] jre not found in target!
)
:: ============================================================
:: 生成 start.bat
:: ============================================================
(
echo @echo off
echo chcp 65001 ^>nul
echo title WorkUtils
echo.
echo :: 优先用内嵌 JRE,否则查系统 Java 17+
echo set "JAVA_BIN=%%~dp0jre\bin\java.exe"
echo if exist "%%JAVA_BIN%%" goto :run
echo.
echo set "JAVA_BIN=java"
echo for /f "tokens=3" %%%%v in ^('"%%JAVA_BIN%%" -version 2^>^&1 ^| findstr /i "version"'^) do set "JAVA_VER=%%%%~v"
echo for /f "tokens=1 delims=." %%%%m in ^("%%JAVA_VER%%"^) do set "MAJOR=%%%%m"
echo if "%%MAJOR%%"=="1" for /f "tokens=2 delims=." %%%%m in ^("%%JAVA_VER%%"^) do set "MAJOR=%%%%m"
echo if %%MAJOR%% LSS 17 ^(
echo echo [ERROR] Java %%JAVA_VER%% found. WorkUtils requires Java 17+.
echo echo Download JDK 17+ from https://adoptium.net/
echo pause ^& exit /b 1
echo ^)
echo.
echo :run
echo "%%JAVA_BIN%%" -jar "%%~dp0workutils-%VERSION%.jar" %%*
echo if %%errorlevel%% neq 0 pause
) > "%DIST%\workutils\start.bat"
echo [OK] start.bat
:: ============================================================
:: 打包 ZIP
:: ============================================================
set "ZIPNAME=workutils-v%VERSION%"
echo 正在压缩 %ZIPNAME%.zip ...
powershell -Command ^
"Compress-Archive -Path '%DIST%\workutils' -DestinationPath '%DIST%\%ZIPNAME%.zip' -Force"
echo [OK] %ZIPNAME%.zip
:: ============================================================
:: 完成
:: ============================================================
echo.
echo ============================================
echo Build Complete ^! v%VERSION%
echo ============================================
echo Output:
echo %DIST%\workutils\
echo %DIST%\%ZIPNAME%.zip
echo ============================================
echo.
set "JAR_NAME=workutils-%VERSION%.jar"
set "JAR_PATH=%TARGET%\%JAR_NAME%"
if not exist "%JAR_PATH%" (
echo [ERROR] Executable JAR not found: %JAR_PATH%
pause
exit /b 1
)
echo Version: %VERSION%
echo.
echo [3/5] Assemble release directory...
if exist "%DIST%" rd /s /q "%DIST%"
mkdir "%RELEASE_DIR%" || goto :build_failed
copy /y "%JAR_PATH%" "%RELEASE_DIR%\%JAR_NAME%" >nul || goto :build_failed
powershell -NoProfile -NonInteractive -Command ^
"$text = Get-Content -Raw -Encoding UTF8 '%PROJECT_DIR%\scripts\start-release.bat';" ^
"$text = $text.Replace('@APP_JAR@', '%JAR_NAME%');" ^
"[IO.File]::WriteAllText('%RELEASE_DIR%\start.bat', $text, [Text.UTF8Encoding]::new($false))"
if errorlevel 1 goto :build_failed
echo.
echo [4/5] Generate bundled Java runtime...
"%JAVA_HOME%\bin\jlink.exe" ^
--module-path "%JAVA_HOME%\jmods" ^
--add-modules ALL-MODULE-PATH ^
--bind-services ^
--strip-debug ^
--no-header-files ^
--no-man-pages ^
--compress=2 ^
--output "%RELEASE_DIR%\runtime"
if errorlevel 1 goto :build_failed
"%RELEASE_DIR%\runtime\bin\java.exe" -version
if errorlevel 1 goto :build_failed
echo.
echo [5/5] Create portable ZIP...
set "ZIP_PATH=%DIST%\workutils-v%VERSION%-windows.zip"
powershell -NoProfile -NonInteractive -Command ^
"Compress-Archive -Path '%RELEASE_DIR%' -DestinationPath '%ZIP_PATH%' -Force"
if errorlevel 1 goto :build_failed
echo.
echo ============================================
echo Build complete. The recipient needs no Java installation.
echo Directory: %RELEASE_DIR%
echo ZIP: %ZIP_PATH%
echo ============================================
exit /b 0
:try_jdk
if defined BUILD_JDK exit /b 0
set "CANDIDATE=%~1"
if not defined CANDIDATE exit /b 0
if not exist "!CANDIDATE!\bin\java.exe" exit /b 0
if not exist "!CANDIDATE!\bin\jlink.exe" exit /b 0
set "CANDIDATE_VERSION="
set "VERSION_FILE=%TEMP%\workutils-java-version-!RANDOM!.txt"
"!CANDIDATE!\bin\java.exe" -version > "!VERSION_FILE!" 2>&1
for /f "tokens=3" %%v in ('findstr /i "version" "!VERSION_FILE!"') do set "CANDIDATE_VERSION=%%~v"
del /q "!VERSION_FILE!" >nul 2>&1
set "CANDIDATE_MAJOR="
for /f "tokens=1 delims=." %%m in ("!CANDIDATE_VERSION!") do set "CANDIDATE_MAJOR=%%m"
if "!CANDIDATE_MAJOR!"=="1" for /f "tokens=2 delims=." %%m in ("!CANDIDATE_VERSION!") do set "CANDIDATE_MAJOR=%%m"
if not defined CANDIDATE_MAJOR exit /b 0
if !CANDIDATE_MAJOR! GEQ 17 set "BUILD_JDK=!CANDIDATE!"
exit /b 0
:build_failed
echo.
echo [ERROR] Windows portable release build failed.
pause
exit /b 1
+21
View File
@@ -0,0 +1,21 @@
@echo off
chcp 65001 >nul
setlocal
cd /d "%~dp0"
set "JAVA_BIN=%~dp0runtime\bin\java.exe"
set "APP_JAR=%~dp0@APP_JAR@"
if not exist "%JAVA_BIN%" (
echo [ERROR] Bundled Java runtime is missing: %JAVA_BIN%
pause
exit /b 1
)
if not exist "%APP_JAR%" (
echo [ERROR] WorkUtils JAR is missing: %APP_JAR%
pause
exit /b 1
)
"%JAVA_BIN%" -Dfile.encoding=UTF-8 "-Dworkutils.home=%~dp0" -jar "%APP_JAR%" %*
if errorlevel 1 pause
@@ -30,13 +30,14 @@ class BuildPortabilityTest {
}
@Test
void launch4jOnlyBelongsToTheWindowsProfile() throws Exception {
void launch4jRequiresAnExplicitWindowsPackageProfile() 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"));
Element property = directChild(directChild(windowsProfile, "activation"), "property");
assertEquals("windows.package", childText(property, "name"));
assertTrue(hasPlugin(directChild(windowsProfile, "build"), "launch4j-maven-plugin"));
}