├── .github └── issue_template.md ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── docs ├── dmg-properties-explained.png ├── gradle │ └── plugin-configuration-samples.md ├── linux-specific-properties.md ├── macosx-specific-properties.md ├── manifest.md ├── maven │ └── plugin-configuration-samples.md ├── windows-specific-properties.md └── windows-tools-guide.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── it ├── settings.xml └── simple-it │ ├── pom.xml │ └── verify.groovy └── main ├── java ├── io │ └── github │ │ └── fvarrui │ │ └── javapackager │ │ ├── gradle │ │ ├── AbstractPackageTask.java │ │ ├── CopyDependencies.java │ │ ├── CreateRunnableJar.java │ │ ├── CreateTarball.java │ │ ├── CreateWindowsExeLaunch4j.java │ │ ├── CreateZipball.java │ │ ├── GradleContext.java │ │ ├── PackagePlugin.java │ │ ├── PackagePluginExtension.java │ │ └── PackageTask.java │ │ ├── maven │ │ ├── CopyDependencies.java │ │ ├── CreateRunnableJar.java │ │ ├── CreateTarball.java │ │ ├── CreateWindowsExeLaunch4j.java │ │ ├── CreateZipball.java │ │ ├── MavenContext.java │ │ ├── PackageMojo.java │ │ └── ResolveLicenseFromPOM.java │ │ ├── model │ │ ├── Arch.java │ │ ├── CFBundlePackageType.java │ │ ├── FileAssociation.java │ │ ├── HeaderType.java │ │ ├── InfoPlist.java │ │ ├── LinuxConfig.java │ │ ├── MacConfig.java │ │ ├── MacStartup.java │ │ ├── Manifest.java │ │ ├── ManifestSection.java │ │ ├── Platform.java │ │ ├── Registry.java │ │ ├── RegistryEntry.java │ │ ├── Scripts.java │ │ ├── SetupMode.java │ │ ├── Template.java │ │ ├── ValueType.java │ │ ├── WindowsConfig.java │ │ ├── WindowsExeCreationTool.java │ │ └── WindowsSigning.java │ │ ├── packagers │ │ ├── AbstractCreateWindowsExe.java │ │ ├── ArtifactGenerator.java │ │ ├── BundleJre.java │ │ ├── Context.java │ │ ├── CreateWindowsExeWhy.java │ │ ├── CreateWindowsExeWinRun4j.java │ │ ├── GenerateAppImage.java │ │ ├── GenerateDeb.java │ │ ├── GenerateDmg.java │ │ ├── GenerateMsi.java │ │ ├── GenerateMsm.java │ │ ├── GeneratePkg.java │ │ ├── GenerateRpm.java │ │ ├── GenerateSetup.java │ │ ├── LinuxPackager.java │ │ ├── MacPackager.java │ │ ├── Packager.java │ │ ├── PackagerFactory.java │ │ ├── PackagerSettings.java │ │ └── WindowsPackager.java │ │ └── utils │ │ ├── CharsetUtil.java │ │ ├── CommandUtils.java │ │ ├── Commandline.java │ │ ├── ExecutionResult.java │ │ ├── FileUtils.java │ │ ├── IconUtils.java │ │ ├── JDKUtils.java │ │ ├── JarUtils.java │ │ ├── Logger.java │ │ ├── MojoExecutorUtils.java │ │ ├── ObjectUtils.java │ │ ├── RcEdit.java │ │ ├── StringUtils.java │ │ ├── ThreadUtils.java │ │ ├── VelocityUtils.java │ │ ├── VersionUtils.java │ │ └── XMLUtils.java └── net │ └── jsign │ └── WindowsSigner.java └── resources ├── linux ├── assembly.xml.vtl ├── control.vtl ├── default-icon.png ├── desktop-appimage.vtl ├── desktop.vtl ├── mime.xml.vtl └── startup.sh.vtl ├── mac ├── Info.plist.vtl ├── RuntimeInfo.plist.vtl ├── assembly.xml.vtl ├── background.png ├── customize-dmg.applescript.vtl ├── default-icon.icns ├── entitlements.plist.vtl ├── startup.vtl ├── universalJavaApplicationStub ├── universalJavaApplicationStub.arm64 ├── universalJavaApplicationStub.sh └── universalJavaApplicationStub.x86_64 └── windows ├── JavaLauncher.exe ├── WinRun4J.exe ├── WinRun4J64.exe ├── assembly.xml.vtl ├── default-icon.ico ├── exe.manifest.vtl ├── ini.vtl ├── iss.vtl ├── msm.wxs.vtl ├── rcedit-x64.exe ├── startup.vbs.vtl ├── why-ini.vtl └── wxs.vtl /.github/issue_template.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/.github/issue_template.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | **/build/ 3 | 4 | # Package Files # 5 | bin 6 | target 7 | .settings/ 8 | .classpath 9 | .project 10 | 11 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 12 | !gradle-wrapper.jar 13 | src/main/resources/windows/winrun4j-launcher.jar 14 | 15 | # Log file 16 | *.log 17 | 18 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 19 | hs_err_pid* 20 | 21 | # IntelliJ 22 | **/.idea/ 23 | **.iml 24 | 25 | # Eclipse 26 | **/.settings/ 27 | 28 | # Maven 29 | **/target/ 30 | **/*.xml.releaseBackup 31 | /release.properties 32 | 33 | ## More 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /docs/dmg-properties-explained.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/docs/dmg-properties-explained.png -------------------------------------------------------------------------------- /docs/gradle/plugin-configuration-samples.md: -------------------------------------------------------------------------------- 1 | # Plugin configuration samples for Gradle 2 | 3 | ## Minimal config 4 | 5 | > :warning: This minimal configuration will not bundle a JRE, so final user will need one in order to run the app. 6 | 7 | ### Using your own task 8 | 9 | Add next task to your `build.gradle` file: 10 | 11 | ```groovy 12 | task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 13 | mainClass = 'fvarrui.sample.Main' 14 | } 15 | ``` 16 | 17 | And run `gradle packageMyApp`. 18 | 19 | ### Using default task 20 | 21 | Default `package` task is configured using `javapackager` extension so, add next to your `build.gradle` file: 22 | 23 | ```groovy 24 | javapackager { 25 | mainClass = 'fvarrio.sample.Main' 26 | } 27 | ``` 28 | And run `gradle package`. 29 | 30 | ## Bundle with a customized JRE 31 | 32 | ```groovy 33 | task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 34 | mainClass = 'fvarrui.sample.Main' 35 | bundleJre = true 36 | } 37 | ``` 38 | 39 | > `customizedJre` is `true` by default, so you don't have to specify it. 40 | 41 | ## Bundle with a full JRE 42 | 43 | ```groovy 44 | task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 45 | mainClass = 'fvarrui.sample.Main' 46 | bundleJre = true 47 | customizedJre = false 48 | } 49 | ``` 50 | 51 | ## Bundle with an existing JRE 52 | 53 | ```groovy 54 | task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 55 | mainClass = 'fvarrui.sample.Main' 56 | bundleJre = true 57 | jrePath = file('C:\Program Files\Java\jre1.8.0_231') 58 | } 59 | ``` 60 | 61 | ## Bundle your own fat JAR 62 | 63 | ```groovy 64 | task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 65 | mainClass = 'fvarrui.sample.Main' 66 | bundleJre = true 67 | runnableJar = file('path/to/your/own/fat.jar') 68 | copyDependencies = false 69 | } 70 | ``` 71 | 72 | ## Multiple executions 73 | 74 | ```groovy 75 | javapackager { 76 | // common configuration 77 | mainClass = 'fvarrui.sample.Main' 78 | } 79 | task packageMyAppWithJRE(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 80 | name = 'Sample' 81 | bundleJre = true 82 | } 83 | task packageMyAppWithoutJRE(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 84 | name = 'Sample-nojre' 85 | bundleJre = false 86 | } 87 | task packageMyApp(dependsOn: [ 'packageMyAppWithJRE', 'packageMyAppWithoutJRE' ]) 88 | ``` 89 | 90 | E.g. on Windows, last configuration will generate next artifacts: 91 | * `Sample_x.y.z.exe` with a bundled JRE. 92 | * `Sample-nojre_x.y.z.exe` without JRE. 93 | 94 | ## Bundling for multiple platforms 95 | 96 | ```groovy 97 | javapackager { 98 | // common configuration 99 | mainClass = 'fvarrui.sample.Main' 100 | bundleJre = true 101 | generateInstaller = false 102 | } 103 | task packageMyAppForLinux(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 104 | platform = linux 105 | createTarball = true 106 | jdkPath = file('X:\\path\to\linux\jdk') 107 | } 108 | task packageMyAppForMac(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 109 | platform = mac 110 | createTarball = true 111 | jdkPath = file('X:\\path\to\mac\jdk') 112 | } 113 | task packageMyAppForWindows(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) { 114 | platform = windows 115 | createZipball = true 116 | } 117 | task packageMyApp(dependsOn: [ 'packageMyAppForLinux', 'packageMyAppForMac', 'packageMyAppForWindows' ]) 118 | ``` 119 | 120 | E.g. on Windows, running `packageMyApp` task will generate next artifacts: 121 | 122 | * `${name}_${version}-linux.tar.gz` with the GNU/Linux application including a customized JRE. 123 | * `${name}_${version}-mac.tar.gz` with the MacOS application including a customized JRE. 124 | * `${name}_${version}-windows.zip` with the Windows application including a customized JRE. 125 | 126 | As last sample is running on Windows, it's not necessary to specify a JDK when bundling for Windows (it uses current JDK by default). Otherwise, if running on GNU/Linux or MacOS, you have to specify a JDK for Windows. 127 | -------------------------------------------------------------------------------- /docs/linux-specific-properties.md: -------------------------------------------------------------------------------- 1 | # GNU/Linux specific properties 2 | 3 | ```xml 4 | 5 | path/to/icon.png 6 | true|false 7 | true|false 8 | true|false 9 | true|false 10 | 11 | Utility 12 | ... 13 | 14 | 15 | ``` 16 | 17 | | Property | Mandatory | Default value | Description | 18 | | ------------------ | --------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------- | 19 | | `pngFile` | :x: | `null` | Icon file. | 20 | | `generateAppImage` | :x: | `true` | [AppImage](https://appimage.org/) package will be generated. | 21 | | `generateDeb` | :x: | `true` | DEB package will be generated. | 22 | | `generateRpm` | :x: | `true` | RPM package will be generated. | 23 | | `wrapJar` | :x: | `true` | Wraps JAR file inside the executable if `true`. | 24 | | `categories` | :x: | `[ "Utility"]` | [Main categories](https://specifications.freedesktop.org/menu-spec/latest/apa.html) in the application's desktop entry file. | 25 | -------------------------------------------------------------------------------- /docs/manifest.md: -------------------------------------------------------------------------------- 1 | # *manifest* property 2 | 3 | `manifest` property allows adding additional manifest entries and sections. 4 | 5 | ## Maven 6 | 7 | ```xml 8 | 9 | 10 | Peter 11 | 12 | 13 |
14 | foo/ 15 | 16 | foo1 17 | 18 |
19 |
20 |
21 | ``` 22 | 23 | ## Gradle 24 | 25 | ```groovy 26 | manifest { 27 | additionalEntries = [ 28 | 'Created-By': 'Peter' 29 | ] 30 | sections = [ 31 | new io.github.fvarrui.javapackager.model.ManifestSection ([ 32 | name: "foo/", 33 | entries: [ 34 | 'Implementation-Version': 'foo1' 35 | ] 36 | ]) 37 | ] 38 | } 39 | ``` 40 | 41 | ## Result 42 | 43 | Both produce the following `MANIFEST.MF` file: 44 | 45 | ```properties 46 | Manifest-Version: 1.0 47 | Created-By: Peter 48 | 49 | Name: foo/ 50 | Implementation-Version: foo1 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/windows-tools-guide.md: -------------------------------------------------------------------------------- 1 | # Windows tools installation guide 2 | 3 | As explained in the [docs](https://github.com/fvarrui/JavaPackager#generated-artifacts), you must install [Inno Setup (iscc)](https://jrsoftware.org/isinfo.php) to generate an EXE installer and [WIX Toolset (candle and light)](https://wixtoolset.org/) to generate an MSI file. 4 | 5 | For Inno Setup 5.x, the Unicode version should be used, because the scripts that JavaPacker will be creating as an input will be UTF-8 encoded. 6 | 7 | ## Using Chocolatey 8 | You can install both tools in a simple way using [Chocolatey](https://chocolatey.org/) package manager: 9 | 10 | 1. [Install Chocolatey](https://chocolatey.org/install) 11 | 2. Run next command on CMD or PowerShell as Administrator to install both tools: 12 | 13 | ``` 14 | choco install -y innosetup wixtoolset 15 | ``` 16 | 17 | > And both tools will be automatically available in `PATH`. 18 | 19 | ## Using scoop 20 | You can also use [Scoop](https://github.com/ScoopInstaller/Scoop/wiki) to achieve the same goal. Scoop is a lightweight alternative to Chocolatey that doesn't require admin rights and installs by default to a folder in the user's home directory. 21 | 22 | 1. [Install Scoop](https://scoop.sh/) 23 | 2. Run in CMD or PowerShell (no need to be Administrator): 24 | 25 | ``` 26 | scoop bucket add extras 27 | scoop install inno-setup 28 | scoop install wixtoolset 29 | ``` 30 | 31 | > Both tools will also be available in `PATH`. 32 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'javapackager' -------------------------------------------------------------------------------- /src/it/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | it-repo 18 | 19 | true 20 | 21 | 22 | 23 | local.central 24 | @localRepositoryUrl@ 25 | 26 | true 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | local.central 36 | @localRepositoryUrl@ 37 | 38 | true 39 | 40 | 41 | true 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/it/simple-it/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | fvarrui.maven.it 7 | simple-it 8 | 1.0-SNAPSHOT 9 | 10 | A simple IT verifying the basic use case. 11 | 12 | 13 | UTF-8 14 | 15 | 16 | 17 | 18 | 19 | @project.groupId@ 20 | @project.artifactId@ 21 | @project.version@ 22 | 23 | 24 | touch 25 | validate 26 | 27 | touch 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/it/simple-it/verify.groovy: -------------------------------------------------------------------------------- 1 | File touchFile = new File( basedir, "target/touch.txt" ); 2 | 3 | assert touchFile.isFile() 4 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/AbstractPackageTask.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.gradle.api.DefaultTask; 8 | import org.gradle.api.tasks.OutputFiles; 9 | import org.gradle.api.tasks.TaskAction; 10 | 11 | import io.github.fvarrui.javapackager.packagers.Packager; 12 | 13 | /** 14 | * Abstract packaging task for Gradle 15 | */ 16 | public abstract class AbstractPackageTask extends DefaultTask { 17 | 18 | private List outputFiles; 19 | 20 | @OutputFiles 21 | public List getOutputFiles() { 22 | return outputFiles != null ? outputFiles : new ArrayList<>(); 23 | } 24 | 25 | /** 26 | * Task constructor 27 | */ 28 | public AbstractPackageTask() { 29 | super(); 30 | setGroup(PackagePlugin.GROUP_NAME); 31 | setDescription("Packages the application as a native Windows, MacOS or GNU/Linux executable and creates an installer"); 32 | getOutputs().upToDateWhen(o -> false); 33 | } 34 | 35 | /** 36 | * Packaging task action 37 | * @throws Exception Throwed if something went wrong 38 | */ 39 | @TaskAction 40 | public void doPackage() throws Exception { 41 | 42 | Packager packager = createPackager(); 43 | 44 | // generates app, installers and bundles 45 | File app = packager.createApp(); 46 | List installers = packager.generateInstallers(); 47 | List bundles = packager.createBundles(); 48 | 49 | // sets generated files as output 50 | outputFiles = new ArrayList<>(); 51 | outputFiles.add(app); 52 | outputFiles.addAll(installers); 53 | outputFiles.addAll(bundles); 54 | 55 | } 56 | 57 | /** 58 | * Creates a platform specific packager 59 | * @return Packager 60 | * @throws Exception Throwed if something went wrong 61 | */ 62 | protected abstract Packager createPackager() throws Exception; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/CopyDependencies.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | 5 | import org.gradle.api.Project; 6 | import org.gradle.api.tasks.Copy; 7 | 8 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 9 | import io.github.fvarrui.javapackager.packagers.Context; 10 | import io.github.fvarrui.javapackager.packagers.Packager; 11 | 12 | /** 13 | * Copies all dependencies to app folder on Gradle context 14 | */ 15 | public class CopyDependencies extends ArtifactGenerator { 16 | 17 | public Copy copyLibsTask; 18 | 19 | public CopyDependencies() { 20 | super("Libs folder"); 21 | } 22 | 23 | @Override 24 | public boolean skip(Packager packager) { 25 | return !packager.getCopyDependencies(); 26 | } 27 | 28 | @Override 29 | protected File doApply(Packager packager) { 30 | 31 | File libsFolder = new File(packager.getJarFileDestinationFolder(), "libs"); 32 | Project project = Context.getGradleContext().getProject(); 33 | 34 | copyLibsTask = (Copy) project.getTasks().findByName("copyLibs"); 35 | if (copyLibsTask == null) { 36 | copyLibsTask = project.getTasks().create("copyLibs", Copy.class); 37 | } 38 | copyLibsTask.setDuplicatesStrategy(Context.getGradleContext().getDuplicatesStrategy()); 39 | copyLibsTask.from(project.getConfigurations().getByName("runtimeClasspath")); 40 | copyLibsTask.into(project.file(libsFolder)); 41 | copyLibsTask.getActions().forEach(action -> action.execute(copyLibsTask)); 42 | 43 | return libsFolder; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/CreateRunnableJar.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.gradle.api.Project; 11 | import org.gradle.api.tasks.bundling.Jar; 12 | 13 | import io.github.fvarrui.javapackager.model.Manifest; 14 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 15 | import io.github.fvarrui.javapackager.packagers.Context; 16 | import io.github.fvarrui.javapackager.packagers.Packager; 17 | 18 | /** 19 | * Creates a runnable jar file from sources on Maven context 20 | */ 21 | public class CreateRunnableJar extends ArtifactGenerator { 22 | 23 | public CreateRunnableJar() { 24 | super("Runnable JAR"); 25 | } 26 | 27 | @Override 28 | protected File doApply(Packager packager) { 29 | 30 | String classifier = "runnable"; 31 | String name = packager.getName(); 32 | String version = packager.getVersion(); 33 | String mainClass = packager.getMainClass(); 34 | File outputDirectory = packager.getOutputDirectory(); 35 | Project project = Context.getGradleContext().getProject(); 36 | File libsFolder = packager.getLibsFolder(); 37 | Manifest manifest = packager.getManifest(); 38 | 39 | List dependencies = new ArrayList<>(); 40 | if (libsFolder != null && libsFolder.exists()) { 41 | dependencies = Arrays.asList(libsFolder.listFiles()).stream().map(f -> libsFolder.getName() + "/" + f.getName()).collect(Collectors.toList()); 42 | } 43 | 44 | Jar jarTask = (Jar) project.getTasks().findByName("jar"); 45 | jarTask.setProperty("archiveBaseName", name); 46 | jarTask.setProperty("archiveVersion", version); 47 | jarTask.setProperty("archiveClassifier", classifier); 48 | jarTask.setProperty("destinationDirectory", outputDirectory); 49 | jarTask.getManifest().getAttributes().put("Created-By", "Gradle " + Context.getGradleContext().getProject().getGradle().getGradleVersion()); 50 | jarTask.getManifest().getAttributes().put("Built-By", System.getProperty("user.name")); 51 | jarTask.getManifest().getAttributes().put("Build-Jdk", System.getProperty("java.version")); 52 | jarTask.getManifest().getAttributes().put("Class-Path", StringUtils.join(dependencies, " ")); 53 | jarTask.getManifest().getAttributes().put("Main-Class", mainClass); 54 | if (manifest != null) { 55 | jarTask.getManifest().attributes(manifest.getAdditionalEntries()); 56 | manifest.getSections().stream().forEach(s -> jarTask.getManifest().attributes(s.getEntries(), s.getName())); 57 | } 58 | 59 | jarTask.getActions().forEach(action -> action.execute(jarTask)); 60 | 61 | return jarTask.getArchiveFile().get().getAsFile(); 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/CreateTarball.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.UUID; 5 | 6 | import org.gradle.api.tasks.bundling.Compression; 7 | import org.gradle.api.tasks.bundling.Tar; 8 | 9 | import io.github.fvarrui.javapackager.model.Platform; 10 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 11 | import io.github.fvarrui.javapackager.packagers.Context; 12 | import io.github.fvarrui.javapackager.packagers.MacPackager; 13 | import io.github.fvarrui.javapackager.packagers.Packager; 14 | 15 | /** 16 | * Creates tarball (tar.gz file) on Gradle context 17 | */ 18 | public class CreateTarball extends ArtifactGenerator { 19 | 20 | public CreateTarball() { 21 | super("Tarball"); 22 | } 23 | 24 | @Override 25 | public boolean skip(Packager packager) { 26 | return !packager.getCreateTarball(); 27 | } 28 | 29 | @Override 30 | protected File doApply(Packager packager) throws Exception { 31 | 32 | String name = packager.getName(); 33 | String version = packager.getVersion(); 34 | Platform platform = packager.getPlatform(); 35 | File outputDirectory = packager.getOutputDirectory(); 36 | File appFolder = packager.getAppFolder(); 37 | File executable = packager.getExecutable(); 38 | String jreDirectoryName = packager.getJreDirectoryName(); 39 | 40 | // tgz file name 41 | String finalName = packager.getTarballName() != null ? packager.getTarballName() : name + "-" + version + "-" + platform; 42 | String format = ".tar.gz"; 43 | File tarFile = new File(outputDirectory, finalName + format); 44 | 45 | Tar tarTask = createTarTask(); 46 | tarTask.setProperty("archiveFileName", tarFile.getName()); 47 | tarTask.setProperty("destinationDirectory", outputDirectory); 48 | tarTask.setCompression(Compression.GZIP); 49 | 50 | // if zipball is for windows platform 51 | if (Platform.windows.equals(platform)) { 52 | 53 | tarTask.from(appFolder.getParentFile(), copySpec -> { 54 | copySpec.include(appFolder.getName() + "/**"); 55 | }); 56 | 57 | } 58 | 59 | // if zipball is for linux platform 60 | else if (Platform.linux.equals(platform)) { 61 | 62 | tarTask.from(appFolder.getParentFile(), copySpec -> { 63 | copySpec.include(appFolder.getName() + "/**"); 64 | copySpec.exclude(appFolder.getName() + "/" + executable.getName()); 65 | copySpec.exclude(appFolder.getName() + "/" + jreDirectoryName + "/bin/*"); 66 | copySpec.exclude(appFolder.getName() + "/scripts/*"); 67 | }); 68 | tarTask.from(appFolder.getParentFile(), copySpec -> { 69 | copySpec.include(appFolder.getName() + "/" + executable.getName()); 70 | copySpec.include(appFolder.getName() + "/" + jreDirectoryName + "/bin/*"); 71 | copySpec.include(appFolder.getName() + "/scripts/*"); 72 | copySpec.setFileMode(0755); 73 | }); 74 | 75 | } 76 | 77 | // if zipball is for macos platform 78 | else if (Platform.mac.equals(platform)) { 79 | 80 | MacPackager macPackager = (MacPackager) packager; 81 | File appFile = macPackager.getAppFile(); 82 | 83 | tarTask.from(appFolder, copySpec -> { 84 | copySpec.include(appFile.getName() + "/**"); 85 | copySpec.exclude(appFile.getName() + "/Contents/MacOS/" + executable.getName()); 86 | copySpec.exclude(appFile.getName() + "/Contents/MacOS/universalJavaApplicationStub"); 87 | copySpec.exclude(appFile.getName() + "/Contents/PlugIns/" + jreDirectoryName + "/Contents/Home/bin/*"); 88 | copySpec.exclude(appFile.getName() + "/Contents/Resources/scripts/*"); 89 | 90 | }); 91 | tarTask.from(appFolder, copySpec -> { 92 | copySpec.include(appFile.getName() + "/Contents/MacOS/" + executable.getName()); 93 | copySpec.include(appFile.getName() + "/Contents/MacOS/universalJavaApplicationStub"); 94 | copySpec.include(appFile.getName() + "/Contents/PlugIns/" + jreDirectoryName + "/Contents/Home/bin/*"); 95 | copySpec.include(appFile.getName() + "/Contents/Resources/scripts/*"); 96 | copySpec.setFileMode(0755); 97 | }); 98 | 99 | } 100 | 101 | tarTask.getActions().forEach(action -> action.execute(tarTask)); 102 | 103 | return tarFile; 104 | } 105 | 106 | private Tar createTarTask() { 107 | return Context.getGradleContext().getProject().getTasks().create("createTarball_" + UUID.randomUUID(), Tar.class); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/CreateWindowsExeLaunch4j.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | 7 | import net.jsign.WindowsSigner; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask; 11 | import io.github.fvarrui.javapackager.model.WindowsConfig; 12 | import io.github.fvarrui.javapackager.model.WindowsExeCreationTool; 13 | import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe; 14 | import io.github.fvarrui.javapackager.packagers.Context; 15 | import io.github.fvarrui.javapackager.packagers.WindowsPackager; 16 | import io.github.fvarrui.javapackager.utils.FileUtils; 17 | 18 | /** 19 | * Creates Windows native executable on Gradle context 20 | */ 21 | public class CreateWindowsExeLaunch4j extends AbstractCreateWindowsExe { 22 | 23 | public CreateWindowsExeLaunch4j() { 24 | super(WindowsExeCreationTool.launch4j); 25 | } 26 | 27 | @Override 28 | protected File doApply(WindowsPackager packager) throws Exception { 29 | 30 | List vmArgs = packager.getVmArgs(); 31 | WindowsConfig winConfig = packager.getWinConfig(); 32 | File executable = packager.getExecutable(); 33 | String mainClass = packager.getMainClass(); 34 | boolean useResourcesAsWorkingDir = packager.isUseResourcesAsWorkingDir(); 35 | boolean bundleJre = packager.getBundleJre(); 36 | String jreDirectoryName = packager.getJreDirectoryName(); 37 | String jreMinVersion = packager.getJreMinVersion(); 38 | File jarFile = packager.getJarFile(); 39 | File appFolder = packager.getAppFolder(); 40 | 41 | createAssets(packager); // creates a folder only for launch4j assets 42 | 43 | // copies JAR to app folder 44 | String jarPath; 45 | if (winConfig.isWrapJar()) { 46 | jarPath = getGenericJar().getAbsolutePath(); 47 | } else { 48 | FileUtils.copyFileToFolder(jarFile, appFolder); 49 | jarPath = jarFile.getName(); 50 | } 51 | 52 | Launch4jLibraryTask l4jTask = Context.getGradleContext().getLibraryTask(); 53 | l4jTask.getDuplicatesStrategy().set(Context.getGradleContext().getDuplicatesStrategy()); 54 | l4jTask.getOutputs().upToDateWhen(task -> false); 55 | l4jTask.getHeaderType().set(winConfig.getHeaderType().toString()); 56 | l4jTask.getJarFiles().set(Context.getGradleContext().getProject().files(jarPath)); 57 | l4jTask.getDontWrapJar().set(!winConfig.isWrapJar()); 58 | l4jTask.getOutfile().set(getGenericExe().getName()); 59 | l4jTask.getIcon().set(getGenericIcon().getAbsolutePath()); 60 | l4jTask.getManifest().set(getGenericManifest().getAbsolutePath()); 61 | l4jTask.getMainClassName().set(mainClass); 62 | l4jTask.getClasspath().set(new HashSet<>(packager.getClasspaths())); 63 | l4jTask.getChdir().set(useResourcesAsWorkingDir ? "." : ""); 64 | if (bundleJre) { 65 | l4jTask.getBundledJrePath().set(jreDirectoryName); 66 | } 67 | if (!StringUtils.isBlank(jreMinVersion)) { 68 | l4jTask.getJreMinVersion().set(jreMinVersion); 69 | } 70 | l4jTask.getJvmOptions().addAll(vmArgs); 71 | l4jTask.getVersion().set(winConfig.getProductVersion()); 72 | l4jTask.getTextVersion().set(winConfig.getTxtProductVersion()); 73 | l4jTask.getCopyright().set(winConfig.getCopyright()); 74 | l4jTask.getCompanyName().set(winConfig.getCompanyName()); 75 | l4jTask.getFileDescription().set(winConfig.getFileDescription()); 76 | l4jTask.getProductName().set(winConfig.getProductName()); 77 | l4jTask.getInternalName().set(winConfig.getInternalName()); 78 | l4jTask.getTrademarks().set(winConfig.getTrademarks()); 79 | l4jTask.getLanguage().set(winConfig.getLanguage()); 80 | l4jTask.getActions().forEach(action -> action.execute(l4jTask)); 81 | 82 | FileUtils.copyFileToFile(getGenericExe(), executable); 83 | 84 | return createBootstrapScript(packager); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/CreateZipball.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.UUID; 5 | 6 | import org.gradle.api.tasks.bundling.Zip; 7 | 8 | import io.github.fvarrui.javapackager.model.Platform; 9 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 10 | import io.github.fvarrui.javapackager.packagers.Context; 11 | import io.github.fvarrui.javapackager.packagers.MacPackager; 12 | import io.github.fvarrui.javapackager.packagers.Packager; 13 | 14 | /** 15 | * Creates zipball (zip file) on Gradle context 16 | */ 17 | public class CreateZipball extends ArtifactGenerator { 18 | 19 | public CreateZipball() { 20 | super("Zipball"); 21 | } 22 | 23 | @Override 24 | public boolean skip(Packager packager) { 25 | return !packager.getCreateZipball(); 26 | } 27 | 28 | @Override 29 | protected File doApply(Packager packager) throws Exception { 30 | 31 | String name = packager.getName(); 32 | String version = packager.getVersion(); 33 | Platform platform = packager.getPlatform(); 34 | File outputDirectory = packager.getOutputDirectory(); 35 | File appFolder = packager.getAppFolder(); 36 | File executable = packager.getExecutable(); 37 | String jreDirectoryName = packager.getJreDirectoryName(); 38 | 39 | String zipFileName = packager.getZipballName() != null ? packager.getZipballName() : name + "-" + version + "-" + platform + ".zip"; 40 | File zipFile = new File(outputDirectory, zipFileName); 41 | 42 | Zip zipTask = createZipTask(); 43 | zipTask.setProperty("archiveFileName", zipFile.getName()); 44 | zipTask.setProperty("destinationDirectory", outputDirectory); 45 | 46 | // if zipball is for windows platform 47 | if (Platform.windows.equals(platform)) { 48 | 49 | zipTask.from(appFolder.getParentFile(), copySpec -> { 50 | copySpec.include(appFolder.getName() + "/**"); 51 | }); 52 | 53 | } 54 | 55 | // if zipball is for linux platform 56 | else if (Platform.linux.equals(platform)) { 57 | 58 | zipTask.from(appFolder.getParentFile(), copySpec -> { 59 | copySpec.include(appFolder.getName() + "/**"); 60 | copySpec.exclude(appFolder.getName() + "/" + executable.getName()); 61 | copySpec.exclude(appFolder.getName() + "/" + jreDirectoryName + "/bin/*"); 62 | copySpec.exclude(appFolder.getName() + "/scripts/*"); 63 | }); 64 | zipTask.from(appFolder.getParentFile(), copySpec -> { 65 | copySpec.include(appFolder.getName() + "/" + executable.getName()); 66 | copySpec.include(appFolder.getName() + "/" + jreDirectoryName + "/bin/*"); 67 | copySpec.include(appFolder.getName() + "/scripts/*"); 68 | copySpec.setFileMode(0755); 69 | }); 70 | 71 | } 72 | 73 | // if zipball is for macos platform 74 | else if (Platform.mac.equals(platform)) { 75 | 76 | MacPackager macPackager = (MacPackager) packager; 77 | File appFile = macPackager.getAppFile(); 78 | 79 | zipTask.from(appFolder, copySpec -> { 80 | copySpec.include(appFile.getName() + "/**"); 81 | copySpec.exclude(appFile.getName() + "/Contents/MacOS/" + executable.getName()); 82 | copySpec.exclude(appFile.getName() + "/Contents/MacOS/universalJavaApplicationStub"); 83 | copySpec.exclude(appFile.getName() + "/Contents/PlugIns/" + jreDirectoryName + "/Contents/Home/bin/*"); 84 | copySpec.exclude(appFile.getName() + "/Contents/Resources/scripts/*"); 85 | }); 86 | zipTask.from(appFolder, copySpec -> { 87 | copySpec.include(appFile.getName() + "/Contents/MacOS/" + executable.getName()); 88 | copySpec.include(appFile.getName() + "/Contents/MacOS/universalJavaApplicationStub"); 89 | copySpec.include(appFile.getName() + "/Contents/PlugIns/" + jreDirectoryName + "/Contents/Home/bin/*"); 90 | copySpec.include(appFile.getName() + "/Contents/Resources/scripts/*"); 91 | copySpec.setFileMode(0755); 92 | }); 93 | 94 | } 95 | 96 | zipTask.getActions().forEach(action -> action.execute(zipTask)); 97 | 98 | return zipFile; 99 | } 100 | 101 | private Zip createZipTask() { 102 | return Context.getGradleContext().getProject().getTasks().create("createZipball_" + UUID.randomUUID(), Zip.class); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/GradleContext.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.packagers.*; 6 | import org.gradle.api.Project; 7 | import org.gradle.api.file.DuplicatesStrategy; 8 | import org.gradle.api.internal.provider.Providers; 9 | import org.gradle.api.logging.Logger; 10 | import org.gradle.api.plugins.JavaPluginExtension; 11 | import org.gradle.api.provider.Provider; 12 | import org.gradle.jvm.toolchain.JavaLauncher; 13 | import org.gradle.jvm.toolchain.JavaToolchainService; 14 | import org.gradle.jvm.toolchain.JavaToolchainSpec; 15 | 16 | import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask; 17 | 18 | /** 19 | * Gradle context 20 | */ 21 | public class GradleContext extends Context { 22 | 23 | private Project project; 24 | private Launch4jLibraryTask libraryTask; 25 | private DuplicatesStrategy duplicatesStrategy; 26 | 27 | public GradleContext(Project project) { 28 | super(); 29 | this.project = project; 30 | } 31 | 32 | public Logger getLogger() { 33 | return project.getLogger(); 34 | } 35 | 36 | public Project getProject() { 37 | return project; 38 | } 39 | 40 | @Override 41 | public File getRootDir() { 42 | return project.getRootDir(); 43 | } 44 | 45 | @Override 46 | public File getBuildDir() { 47 | return project.getBuildDir(); 48 | } 49 | 50 | @Override 51 | public File createRunnableJar(Packager packager) throws Exception { 52 | return new CreateRunnableJar().apply(packager); 53 | } 54 | 55 | @Override 56 | public File copyDependencies(Packager packager) throws Exception { 57 | return new CopyDependencies().apply(packager); 58 | } 59 | 60 | @Override 61 | public File createTarball(Packager packager) throws Exception { 62 | return new CreateTarball().apply(packager); 63 | } 64 | 65 | @Override 66 | public File createZipball(Packager packager) throws Exception { 67 | return new CreateZipball().apply(packager); 68 | } 69 | 70 | @Override 71 | public File resolveLicense(Packager packager) throws Exception { 72 | // do nothing 73 | return null; 74 | } 75 | 76 | public Launch4jLibraryTask getLibraryTask() { 77 | return libraryTask; 78 | } 79 | 80 | public void setLibraryTask(Launch4jLibraryTask libraryTask) { 81 | this.libraryTask = libraryTask; 82 | } 83 | 84 | public DuplicatesStrategy getDuplicatesStrategy() { 85 | return duplicatesStrategy; 86 | } 87 | 88 | public void setDuplicatesStrategy(DuplicatesStrategy duplicatesStrategy) { 89 | this.duplicatesStrategy = duplicatesStrategy; 90 | } 91 | 92 | /** 93 | * Returns project's default toolchain 94 | * 95 | * @return Default toolchain 96 | */ 97 | public File getDefaultToolchain() { 98 | if (project.getGradle().getGradleVersion().compareTo("7") >= 0) 99 | return getToolchain(); 100 | else 101 | return super.getDefaultToolchain(); 102 | } 103 | 104 | private File getToolchain() { 105 | 106 | // Default toolchain 107 | JavaToolchainSpec toolchain = project.getExtensions().getByType(JavaPluginExtension.class).getToolchain(); 108 | 109 | // acquire a provider that returns the launcher for the toolchain 110 | JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class); 111 | Provider defaultLauncher = service.launcherFor(toolchain).orElse(Providers.notDefined()); 112 | 113 | if (defaultLauncher.isPresent()) { 114 | return defaultLauncher.get().getMetadata().getInstallationPath().getAsFile(); 115 | } 116 | return super.getDefaultToolchain(); 117 | 118 | } 119 | 120 | @Override 121 | public File createWindowsExe(WindowsPackager packager) throws Exception { 122 | AbstractCreateWindowsExe createWindowsExe; 123 | switch (packager.getWinConfig().getExeCreationTool()) { 124 | case launch4j: createWindowsExe = new CreateWindowsExeLaunch4j(); break; 125 | case winrun4j: createWindowsExe = new CreateWindowsExeWinRun4j(); break; 126 | case why: createWindowsExe = new CreateWindowsExeWhy(); break; 127 | default: return null; 128 | } 129 | if (!createWindowsExe.skip(packager)) { 130 | return createWindowsExe.apply(packager); 131 | } 132 | return null; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/PackagePlugin.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.util.UUID; 4 | 5 | import org.gradle.api.Plugin; 6 | import org.gradle.api.Project; 7 | 8 | import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask; 9 | import io.github.fvarrui.javapackager.packagers.Context; 10 | 11 | /** 12 | * JavaPackager Gradle plugin 13 | */ 14 | public class PackagePlugin implements Plugin { 15 | 16 | public static final String GROUP_NAME = "JavaPackager"; 17 | public static final String SETTINGS_EXT_NAME = "javapackager"; 18 | public static final String PACKAGE_TASK_NAME = "package"; 19 | 20 | @Override 21 | public void apply(Project project) { 22 | Context.setContext(new GradleContext(project)); 23 | 24 | project.getPluginManager().apply("java"); 25 | project.getPluginManager().apply("edu.sc.seis.launch4j"); 26 | 27 | project.getExtensions().create(SETTINGS_EXT_NAME, PackagePluginExtension.class, project); 28 | project.getTasks().create(PACKAGE_TASK_NAME, PackageTask.class).dependsOn("build"); 29 | 30 | Context.getGradleContext().setLibraryTask(project.getTasks().create("launch4j_" + UUID.randomUUID(), Launch4jLibraryTask.class)); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/gradle/PackagePluginExtension.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.gradle; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | 7 | import org.gradle.api.Project; 8 | import org.gradle.api.file.DuplicatesStrategy; 9 | 10 | import groovy.lang.Closure; 11 | import io.github.fvarrui.javapackager.model.Arch; 12 | import io.github.fvarrui.javapackager.model.LinuxConfig; 13 | import io.github.fvarrui.javapackager.model.MacConfig; 14 | import io.github.fvarrui.javapackager.model.Manifest; 15 | import io.github.fvarrui.javapackager.model.Platform; 16 | import io.github.fvarrui.javapackager.model.Scripts; 17 | import io.github.fvarrui.javapackager.model.WindowsConfig; 18 | import io.github.fvarrui.javapackager.packagers.PackagerSettings; 19 | 20 | /** 21 | * JavaPackager plugin extension for Gradle 22 | */ 23 | public class PackagePluginExtension extends PackagerSettings { 24 | 25 | private Project project; 26 | private DuplicatesStrategy duplicatesStrategy; 27 | 28 | public PackagePluginExtension(Project project) { 29 | super(); 30 | this.project = project; 31 | this.platform = Platform.auto; 32 | this.additionalModules = new ArrayList<>(); 33 | this.additionalModulePaths = new ArrayList<>(); 34 | this.additionalResources = new ArrayList<>(); 35 | this.administratorRequired = false; 36 | this.assetsDir = new File(project.getProjectDir(), "assets"); 37 | this.bundleJre = true; 38 | this.copyDependencies = true; 39 | this.createTarball = false; 40 | this.createZipball = false; 41 | this.customizedJre = true; 42 | this.description = project.getDescription(); 43 | this.extra = new HashMap<>(); 44 | this.generateInstaller = true; 45 | this.jreDirectoryName = "jre"; 46 | this.linuxConfig = new LinuxConfig(); 47 | this.macConfig = new MacConfig(); 48 | this.manifest = new Manifest(); 49 | this.modules = new ArrayList<>(); 50 | this.name = project.getName(); 51 | this.organizationEmail = ""; 52 | this.useResourcesAsWorkingDir = true; 53 | this.vmArgs = new ArrayList<>(); 54 | this.winConfig = new WindowsConfig(); 55 | this.outputDirectory = project.getBuildDir(); 56 | this.scripts = new Scripts(); 57 | this.forceInstaller = false; 58 | this.arch = Arch.getDefault(); 59 | this.duplicatesStrategy = DuplicatesStrategy.WARN; 60 | } 61 | 62 | public LinuxConfig linuxConfig(Closure closure) { 63 | linuxConfig = new LinuxConfig(); 64 | project.configure(linuxConfig, closure); 65 | return linuxConfig; 66 | } 67 | 68 | public MacConfig macConfig(Closure closure) { 69 | macConfig = new MacConfig(); 70 | project.configure(macConfig, closure); 71 | return macConfig; 72 | } 73 | 74 | public WindowsConfig winConfig(Closure closure) { 75 | winConfig = new WindowsConfig(); 76 | project.configure(winConfig, closure); 77 | return winConfig; 78 | } 79 | 80 | public Manifest manifest(Closure closure) { 81 | manifest = new Manifest(); 82 | project.configure(manifest, closure); 83 | return manifest; 84 | } 85 | 86 | public Scripts scripts(Closure closure) { 87 | scripts = new Scripts(); 88 | project.configure(scripts, closure); 89 | return scripts; 90 | } 91 | 92 | public void setDuplicatesStrategy(DuplicatesStrategy duplicatesStrategy) { 93 | this.duplicatesStrategy = duplicatesStrategy; 94 | } 95 | 96 | public DuplicatesStrategy getDuplicatesStrategy() { 97 | return duplicatesStrategy; 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/CopyDependencies.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 4 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 5 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 6 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 7 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 9 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 10 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 11 | 12 | import java.io.File; 13 | 14 | import org.apache.maven.plugin.MojoExecutionException; 15 | 16 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 17 | import io.github.fvarrui.javapackager.packagers.Context; 18 | import io.github.fvarrui.javapackager.packagers.Packager; 19 | 20 | /** 21 | * Copies all dependencies to app folder on Maven context 22 | */ 23 | public class CopyDependencies extends ArtifactGenerator { 24 | 25 | public CopyDependencies() { 26 | super("Dependencies"); 27 | } 28 | 29 | @Override 30 | public boolean skip(Packager packager) { 31 | return !packager.getCopyDependencies(); 32 | } 33 | 34 | @Override 35 | protected File doApply(Packager packager) { 36 | 37 | File libsFolder = new File(packager.getJarFileDestinationFolder(), "libs"); 38 | 39 | // invokes 'maven-dependency-plugin' plugin to copy dependecies to app libs folder 40 | try { 41 | 42 | executeMojo( 43 | plugin( 44 | groupId("org.apache.maven.plugins"), 45 | artifactId("maven-dependency-plugin"), 46 | version("3.1.1") 47 | ), 48 | goal("copy-dependencies"), 49 | configuration( 50 | element("outputDirectory", libsFolder.getAbsolutePath()) 51 | ), 52 | Context.getMavenContext().getEnv() 53 | ); 54 | 55 | } catch (MojoExecutionException e) { 56 | 57 | throw new RuntimeException("Error copying dependencies: " + e.getMessage()); 58 | 59 | } 60 | 61 | return libsFolder; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/CreateRunnableJar.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 4 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 5 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 6 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 7 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 9 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 10 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 11 | 12 | import java.io.File; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | import org.apache.maven.plugin.MojoExecutionException; 18 | import org.twdata.maven.mojoexecutor.MojoExecutor.Element; 19 | import org.twdata.maven.mojoexecutor.MojoExecutor.ExecutionEnvironment; 20 | 21 | import io.github.fvarrui.javapackager.model.Manifest; 22 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 23 | import io.github.fvarrui.javapackager.packagers.Context; 24 | import io.github.fvarrui.javapackager.packagers.Packager; 25 | import io.github.fvarrui.javapackager.utils.Logger; 26 | import io.github.fvarrui.javapackager.utils.MojoExecutorUtils; 27 | 28 | /** 29 | * Creates a runnable jar file from sources on Maven context 30 | */ 31 | public class CreateRunnableJar extends ArtifactGenerator { 32 | 33 | public CreateRunnableJar() { 34 | super("Runnable JAR"); 35 | } 36 | 37 | @Override 38 | protected File doApply(Packager packager) { 39 | 40 | String classifier = "runnable"; 41 | String mainClass = packager.getMainClass(); 42 | File outputDirectory = packager.getOutputDirectory(); 43 | ExecutionEnvironment env = Context.getMavenContext().getEnv(); 44 | Manifest manifest = packager.getManifest(); 45 | 46 | List archive = new ArrayList<>(); 47 | archive.add( 48 | element("manifest", 49 | element("addClasspath", "true"), 50 | element("classpathPrefix", "libs/"), 51 | element("mainClass", mainClass), 52 | element("useUniqueVersions", "false") 53 | ) 54 | ); 55 | if (manifest != null) { 56 | 57 | archive.add(MojoExecutorUtils.mapToElement("manifestEntries", manifest.getAdditionalEntries())); 58 | 59 | List manifestSections = 60 | manifest 61 | .getSections() 62 | .stream() 63 | .map(s -> element("manifestSection", 64 | element("Name", s.getName()), 65 | MojoExecutorUtils.mapToElement("manifestEntries", s.getEntries()) 66 | )) 67 | .collect(Collectors.toList()); 68 | 69 | archive.add(element("manifestSections", manifestSections.toArray(new Element[manifestSections.size()]))); 70 | 71 | } 72 | 73 | try { 74 | 75 | executeMojo( 76 | plugin( 77 | groupId("org.apache.maven.plugins"), 78 | artifactId("maven-jar-plugin"), 79 | version("3.3.0") 80 | ), 81 | goal("jar"), 82 | configuration( 83 | element("classifier", classifier), 84 | element("archive", archive.toArray(new Element[archive.size()])), 85 | element("outputDirectory", outputDirectory.getAbsolutePath()) 86 | ), 87 | env 88 | ); 89 | 90 | } catch (MojoExecutionException e) { 91 | 92 | Logger.error("Runnable jar creation failed! " + e.getMessage()); 93 | throw new RuntimeException(e); 94 | 95 | } 96 | 97 | // gets build.finalName value 98 | String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName(); 99 | 100 | // creates file pointing to generated jar file 101 | return new File(outputDirectory, finalName + "-" + classifier + ".jar"); 102 | 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/CreateTarball.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 4 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 5 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 6 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 7 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 9 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 10 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 11 | 12 | import java.io.File; 13 | 14 | import io.github.fvarrui.javapackager.model.Platform; 15 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 16 | import io.github.fvarrui.javapackager.packagers.Context; 17 | import io.github.fvarrui.javapackager.packagers.Packager; 18 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 19 | 20 | /** 21 | * Creates tarball (tar.gz file) on Maven context 22 | */ 23 | public class CreateTarball extends ArtifactGenerator { 24 | 25 | public CreateTarball() { 26 | super("Tarball"); 27 | } 28 | 29 | @Override 30 | public boolean skip(Packager packager) { 31 | return !packager.getCreateTarball(); 32 | } 33 | 34 | @Override 35 | protected File doApply(Packager packager) { 36 | 37 | File assetsFolder = packager.getAssetsFolder(); 38 | Platform platform = packager.getPlatform(); 39 | File outputDirectory = packager.getOutputDirectory(); 40 | 41 | try { 42 | 43 | // generate assembly.xml file 44 | File assemblyFile = new File(assetsFolder, "assembly-tarball-" + platform + ".xml"); 45 | VelocityUtils.render(platform + "/assembly.xml.vtl", assemblyFile, packager); 46 | 47 | // output file format 48 | String format = "tar.gz"; 49 | 50 | // invokes plugin to assemble tarball 51 | executeMojo( 52 | plugin( 53 | groupId("org.apache.maven.plugins"), 54 | artifactId("maven-assembly-plugin"), 55 | version("3.1.1") 56 | ), 57 | goal("single"), 58 | configuration( 59 | element("outputDirectory", outputDirectory.getAbsolutePath()), 60 | element("formats", element("format", format)), 61 | element("descriptors", element("descriptor", assemblyFile.getAbsolutePath())), 62 | element("appendAssemblyId", "false") 63 | ), 64 | Context.getMavenContext().getEnv() 65 | ); 66 | 67 | // get generated filename 68 | String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName(); 69 | File finalFile = new File(outputDirectory, finalName + "." + format); 70 | 71 | // get desired file name 72 | String tarName = packager.getTarballName() != null ? packager.getTarballName() : finalName + "-" + platform; 73 | File tarFile = new File(outputDirectory, tarName + "." + format); 74 | 75 | // rename generated to desired 76 | finalFile.renameTo(tarFile); 77 | 78 | return tarFile; 79 | 80 | } catch (Exception e) { 81 | 82 | throw new RuntimeException(e); 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/CreateWindowsExeLaunch4j.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 4 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 5 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 6 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 7 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 9 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 10 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 11 | 12 | import java.io.File; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | import net.jsign.WindowsSigner; 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.twdata.maven.mojoexecutor.MojoExecutor.Element; 20 | 21 | import io.github.fvarrui.javapackager.model.Arch; 22 | import io.github.fvarrui.javapackager.model.WindowsConfig; 23 | import io.github.fvarrui.javapackager.model.WindowsExeCreationTool; 24 | import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe; 25 | import io.github.fvarrui.javapackager.packagers.Context; 26 | import io.github.fvarrui.javapackager.packagers.WindowsPackager; 27 | import io.github.fvarrui.javapackager.utils.FileUtils; 28 | import io.github.fvarrui.javapackager.utils.Logger; 29 | 30 | /** 31 | * Creates Windows executable with Maven 32 | */ 33 | public class CreateWindowsExeLaunch4j extends AbstractCreateWindowsExe { 34 | 35 | public CreateWindowsExeLaunch4j() { 36 | super(WindowsExeCreationTool.launch4j); 37 | } 38 | 39 | @Override 40 | protected File doApply(WindowsPackager packager) throws Exception { 41 | 42 | List vmArgs = packager.getVmArgs(); 43 | WindowsConfig winConfig = packager.getWinConfig(); 44 | File executable = packager.getExecutable(); 45 | String mainClass = packager.getMainClass(); 46 | boolean useResourcesAsWorkingDir = packager.isUseResourcesAsWorkingDir(); 47 | boolean bundleJre = packager.getBundleJre(); 48 | String jreDirectoryName = packager.getJreDirectoryName(); 49 | String classpath = packager.getClasspath(); 50 | String jreMinVersion = packager.getJreMinVersion(); 51 | File jarFile = packager.getJarFile(); 52 | File appFolder = packager.getAppFolder(); 53 | Arch arch = packager.getArch(); 54 | 55 | createAssets(packager); 56 | 57 | // warns about architecture 58 | if (arch != Arch.x86) { 59 | Logger.warn("Launch4J only can generate 32-bit executable"); 60 | } 61 | 62 | // copies JAR to app folder 63 | String jarPath; 64 | if (winConfig.isWrapJar()) { 65 | jarPath = getGenericJar().getAbsolutePath(); 66 | } else { 67 | FileUtils.copyFileToFolder(jarFile, appFolder); 68 | jarPath = jarFile.getName(); 69 | } 70 | 71 | List jreElements = new ArrayList<>(); 72 | jreElements.add(element("opts", vmArgs.stream().map(arg -> element("opt", arg)).toArray(Element[]::new))); 73 | jreElements.add(element("path", bundleJre ? jreDirectoryName : "%JAVA_HOME%;%PATH%")); 74 | if (!StringUtils.isBlank(jreMinVersion)) { 75 | jreElements.add(element("minVersion", jreMinVersion)); 76 | } 77 | 78 | List pluginConfig = new ArrayList<>(); 79 | pluginConfig.add(element("headerType", "" + winConfig.getHeaderType())); 80 | pluginConfig.add(element("jar", jarPath)); 81 | pluginConfig.add(element("dontWrapJar", "" + !winConfig.isWrapJar())); 82 | pluginConfig.add(element("outfile", getGenericExe().getAbsolutePath())); 83 | pluginConfig.add(element("icon", getGenericIcon().getAbsolutePath())); 84 | pluginConfig.add(element("manifest", getGenericManifest().getAbsolutePath())); 85 | pluginConfig.add( 86 | element("classPath", 87 | element("mainClass", mainClass), 88 | element("preCp", classpath), 89 | element("addDependencies", "false") 90 | ) 91 | ); 92 | pluginConfig.add(element("chdir", useResourcesAsWorkingDir ? "." : "")); 93 | pluginConfig.add(element("jre", jreElements.toArray(new Element[0]))); 94 | pluginConfig.add( 95 | element("versionInfo", 96 | element("fileVersion", winConfig.getFileVersion()), 97 | element("txtFileVersion", winConfig.getTxtFileVersion()), 98 | element("productVersion", winConfig.getProductVersion()), 99 | element("txtProductVersion", winConfig.getTxtProductVersion()), 100 | element("copyright", winConfig.getCopyright()), 101 | element("companyName", winConfig.getCompanyName()), 102 | element("fileDescription", winConfig.getFileDescription()), 103 | element("productName", winConfig.getProductName()), 104 | element("internalName", winConfig.getInternalName()), 105 | element("originalFilename", winConfig.getOriginalFilename()), 106 | element("trademarks", winConfig.getTrademarks()), 107 | element("language", winConfig.getLanguage()) 108 | ) 109 | ); 110 | 111 | // invokes launch4j plugin to generate windows executable 112 | try { 113 | 114 | executeMojo( 115 | plugin( 116 | groupId("com.akathist.maven.plugins.launch4j"), 117 | artifactId("launch4j-maven-plugin"), 118 | version("2.4.1") 119 | ), 120 | goal("launch4j"), 121 | configuration(pluginConfig.toArray(new Element[0])), 122 | Context.getMavenContext().getEnv() 123 | ); 124 | 125 | FileUtils.copyFileToFile(getGenericExe(), executable); 126 | 127 | } catch (Exception ex) { 128 | throw new RuntimeException(ex); 129 | } 130 | 131 | return createBootstrapScript(packager); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/CreateZipball.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 4 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 5 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 6 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 7 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 9 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 10 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 11 | 12 | import java.io.File; 13 | 14 | import io.github.fvarrui.javapackager.model.Platform; 15 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 16 | import io.github.fvarrui.javapackager.packagers.Context; 17 | import io.github.fvarrui.javapackager.packagers.Packager; 18 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 19 | 20 | /** 21 | * Creates zipball (zip file) on Maven context 22 | */ 23 | public class CreateZipball extends ArtifactGenerator { 24 | 25 | public CreateZipball() { 26 | super("Zipball"); 27 | } 28 | 29 | @Override 30 | public boolean skip(Packager packager) { 31 | return !packager.getCreateZipball(); 32 | } 33 | 34 | @Override 35 | protected File doApply(Packager packager) { 36 | 37 | File assetsFolder = packager.getAssetsFolder(); 38 | Platform platform = packager.getPlatform(); 39 | File outputDirectory = packager.getOutputDirectory(); 40 | 41 | try { 42 | 43 | // generate assembly.xml file 44 | File assemblyFile = new File(assetsFolder, "assembly-zipball-" + platform + ".xml"); 45 | VelocityUtils.render(platform + "/assembly.xml.vtl", assemblyFile, packager); 46 | 47 | // zip file name and format 48 | String format = "zip"; 49 | 50 | // invokes plugin to assemble zipball and/or tarball 51 | executeMojo( 52 | plugin( 53 | groupId("org.apache.maven.plugins"), 54 | artifactId("maven-assembly-plugin"), 55 | version("3.1.1") 56 | ), 57 | goal("single"), 58 | configuration( 59 | element("outputDirectory", outputDirectory.getAbsolutePath()), 60 | element("formats", element("format", format)), 61 | element("descriptors", element("descriptor", assemblyFile.getAbsolutePath())), 62 | element("appendAssemblyId", "false") 63 | ), 64 | Context.getMavenContext().getEnv() 65 | ); 66 | 67 | // gets generated filename 68 | String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName(); 69 | File finalFile = new File(outputDirectory, finalName + "." + format); 70 | 71 | // gets desired file name 72 | String zipName = packager.getZipballName() != null ? packager.getZipballName() : finalName + "-" + platform; 73 | File zipFile = new File(outputDirectory, zipName + "." + format); 74 | 75 | // rename generated to desired 76 | finalFile.renameTo(zipFile); 77 | 78 | return zipFile; 79 | 80 | } catch (Exception e) { 81 | 82 | throw new RuntimeException(e); 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/MavenContext.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import java.io.File; 4 | import java.time.Year; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.apache.maven.model.Organization; 8 | import org.apache.maven.plugin.logging.Log; 9 | import org.apache.maven.project.MavenProject; 10 | import org.twdata.maven.mojoexecutor.MojoExecutor.ExecutionEnvironment; 11 | 12 | import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe; 13 | import io.github.fvarrui.javapackager.packagers.Context; 14 | import io.github.fvarrui.javapackager.packagers.CreateWindowsExeWhy; 15 | import io.github.fvarrui.javapackager.packagers.CreateWindowsExeWinRun4j; 16 | import io.github.fvarrui.javapackager.packagers.Packager; 17 | import io.github.fvarrui.javapackager.packagers.WindowsPackager; 18 | 19 | /** 20 | * Maven context 21 | */ 22 | public class MavenContext extends Context { 23 | 24 | private Log logger; 25 | private ExecutionEnvironment env; 26 | 27 | public MavenContext(ExecutionEnvironment env, Log logger) { 28 | super(); 29 | this.env = env; 30 | this.logger = logger; 31 | 32 | // initialize some default params on project (avoid launch4j-maven-plugin warnings) 33 | MavenProject project = env.getMavenProject(); 34 | if (project.getOrganization() == null) { 35 | project.setOrganization(new Organization()); 36 | } 37 | // set default organization name 38 | if (StringUtils.isBlank(project.getOrganization().getName())) { 39 | project.getOrganization().setName(Packager.DEFAULT_ORGANIZATION_NAME); 40 | } 41 | // set default inception year 42 | if (StringUtils.isBlank(project.getInceptionYear())) { 43 | project.setInceptionYear(Year.now().toString()); 44 | } 45 | // set default description 46 | if (StringUtils.isBlank(project.getDescription())) { 47 | project.setDescription(project.getArtifactId()); 48 | } 49 | } 50 | 51 | public ExecutionEnvironment getEnv() { 52 | return env; 53 | } 54 | 55 | public Log getLogger() { 56 | return logger; 57 | } 58 | 59 | @Override 60 | public File getRootDir() { 61 | return env.getMavenProject().getBasedir(); 62 | } 63 | 64 | @Override 65 | public File getBuildDir() { 66 | return new File(env.getMavenProject().getBuild().getDirectory()); 67 | } 68 | 69 | @Override 70 | public File createRunnableJar(Packager packager) throws Exception { 71 | return new CreateRunnableJar().apply(packager); 72 | } 73 | 74 | @Override 75 | public File copyDependencies(Packager packager) throws Exception { 76 | return new CopyDependencies().apply(packager); 77 | } 78 | 79 | @Override 80 | public File createTarball(Packager packager) throws Exception { 81 | return new CreateTarball().apply(packager); 82 | } 83 | 84 | @Override 85 | public File createZipball(Packager packager) throws Exception { 86 | return new CreateZipball().apply(packager); 87 | } 88 | 89 | @Override 90 | public File resolveLicense(Packager packager) throws Exception { 91 | return new ResolveLicenseFromPOM().apply(packager); 92 | } 93 | 94 | @Override 95 | public File createWindowsExe(WindowsPackager packager) throws Exception { 96 | AbstractCreateWindowsExe createWindowsExe; 97 | switch (packager.getWinConfig().getExeCreationTool()) { 98 | case launch4j: createWindowsExe = new CreateWindowsExeLaunch4j(); break; 99 | case why: createWindowsExe = new CreateWindowsExeWhy(); break; 100 | case winrun4j: createWindowsExe = new CreateWindowsExeWinRun4j(); break; 101 | default: return null; 102 | } 103 | if (!createWindowsExe.skip(packager)) { 104 | return createWindowsExe.apply(packager); 105 | } 106 | return null; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/maven/ResolveLicenseFromPOM.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.maven; 2 | 3 | import java.io.File; 4 | import java.net.MalformedURLException; 5 | import java.net.URI; 6 | import java.net.URISyntaxException; 7 | import java.net.URL; 8 | import java.util.List; 9 | 10 | import org.apache.maven.model.License; 11 | 12 | import io.github.fvarrui.javapackager.packagers.ArtifactGenerator; 13 | import io.github.fvarrui.javapackager.packagers.Context; 14 | import io.github.fvarrui.javapackager.packagers.Packager; 15 | import io.github.fvarrui.javapackager.utils.FileUtils; 16 | import io.github.fvarrui.javapackager.utils.Logger; 17 | 18 | /** 19 | * Creates a runnable jar file from sources on Maven context 20 | */ 21 | public class ResolveLicenseFromPOM extends ArtifactGenerator { 22 | 23 | public ResolveLicenseFromPOM() { 24 | super("LICENSE"); 25 | } 26 | 27 | @Override 28 | protected File doApply(Packager packager) { 29 | Logger.infoIndent("Trying to resolve license from POM ..."); 30 | 31 | File licenseFile = packager.getLicenseFile(); 32 | List licenses = Context.getMavenContext().getEnv().getMavenProject().getLicenses(); 33 | File assetsFolder = packager.getAssetsFolder(); 34 | 35 | // if license not specified, gets from pom 36 | if (licenseFile == null && !licenses.isEmpty()) { 37 | String urlStr = null; 38 | try { 39 | urlStr = licenses.get(0).getUrl(); 40 | URL licenseUrl = new URI(urlStr).toURL(); 41 | licenseFile = new File(assetsFolder, "LICENSE"); 42 | FileUtils.downloadFromUrl(licenseUrl, licenseFile); 43 | } catch (URISyntaxException | MalformedURLException e) { 44 | Logger.error("Invalid license URL specified: " + urlStr); 45 | licenseFile = null; 46 | } catch (Exception e) { 47 | Logger.error("Cannot download license from " + urlStr); 48 | licenseFile = null; 49 | } 50 | } 51 | 52 | if (licenseFile != null) 53 | Logger.infoUnindent("License resolved " + licenseFile + "!"); 54 | else 55 | Logger.infoUnindent("License not resolved!"); 56 | 57 | return licenseFile; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Arch.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | import org.redline_rpm.header.Architecture; 5 | 6 | public enum Arch { 7 | aarch64, 8 | x64, 9 | x86; 10 | 11 | public static Arch getArch(String archString) { 12 | switch (archString) { 13 | case "x86": 14 | case "i386": 15 | case "i486": 16 | case "i586": 17 | case "i686": 18 | return x86; 19 | case "x86_64": 20 | case "amd64": 21 | return x64; 22 | case "aarch64": 23 | return aarch64; 24 | default: 25 | throw new IllegalArgumentException("Unknown architecture " + archString); 26 | } 27 | } 28 | 29 | public static Arch getDefault() { 30 | return getArch(SystemUtils.OS_ARCH); 31 | } 32 | 33 | public String toDebArchitecture() { 34 | switch (this) { 35 | case aarch64: return "arm64"; 36 | case x64: return "amd64"; 37 | case x86: return "i386"; 38 | default: return null; 39 | } 40 | } 41 | 42 | public Architecture toRpmArchitecture() { 43 | switch (this) { 44 | case aarch64: return Architecture.AARCH64; 45 | case x64: return Architecture.X86_64; 46 | case x86: return Architecture.I386; 47 | default: return null; 48 | } 49 | } 50 | 51 | public String toMsiArchitecture() { 52 | switch (this) { 53 | case aarch64: return "arm64"; 54 | case x64: return "x64"; 55 | case x86: return "x86"; 56 | default: return null; 57 | } 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/CFBundlePackageType.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public enum CFBundlePackageType implements Serializable { 6 | BNDL, 7 | APPL, 8 | FMWK 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class FileAssociation implements Serializable { 6 | private static final long serialVersionUID = -2777769245508048627L; 7 | 8 | private String mimeType; 9 | private String extension; 10 | private String description; 11 | 12 | public String getMimeType() { 13 | return mimeType; 14 | } 15 | 16 | public void setMimeType(String mimeType) { 17 | this.mimeType = mimeType; 18 | } 19 | 20 | public String getExtension() { 21 | return extension; 22 | } 23 | 24 | public void setExtension(String extension) { 25 | this.extension = extension; 26 | } 27 | 28 | public String getDescription() { 29 | return description; 30 | } 31 | 32 | public void setDescription(String description) { 33 | this.description = description; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "FileAssociation [mimeType=" + mimeType + ", extension=" + extension + ", description=" + description 39 | + "]"; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/HeaderType.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | /** 4 | * Windows EXE header type 5 | */ 6 | public enum HeaderType { 7 | gui, 8 | console; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/InfoPlist.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class InfoPlist implements Serializable { 6 | private static final long serialVersionUID = -1237535573669475709L; 7 | 8 | private String additionalEntries = ""; 9 | private CFBundlePackageType bundlePackageType = CFBundlePackageType.BNDL; 10 | 11 | public String getAdditionalEntries() { 12 | return additionalEntries; 13 | } 14 | 15 | public void setAdditionalEntries(String additionalEntries) { 16 | this.additionalEntries = additionalEntries; 17 | } 18 | 19 | public CFBundlePackageType getBundlePackageType() { 20 | return bundlePackageType; 21 | } 22 | 23 | public void setBundlePackageType(CFBundlePackageType bundlePackageType) { 24 | this.bundlePackageType = bundlePackageType; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "InfoPlist [additionalEntries=" + additionalEntries + ", bundlePackageType=" + bundlePackageType + "]"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | import java.util.Arrays; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import org.apache.commons.lang3.ObjectUtils; 10 | 11 | import io.github.fvarrui.javapackager.packagers.Packager; 12 | 13 | /** 14 | * JavaPackager GNU/Linux specific configuration 15 | */ 16 | public class LinuxConfig implements Serializable { 17 | private static final long serialVersionUID = -1238166997019141904L; 18 | 19 | private List categories; 20 | private boolean generateDeb = true; 21 | private boolean generateRpm = true; 22 | private boolean generateAppImage = true; 23 | private File pngFile; 24 | private boolean wrapJar = true; 25 | private String installationPath; 26 | 27 | public void setCategories(List categories) { 28 | this.categories = categories; 29 | } 30 | 31 | public List getCategories() { 32 | return categories; 33 | } 34 | 35 | public boolean isGenerateDeb() { 36 | return generateDeb; 37 | } 38 | 39 | public void setGenerateDeb(boolean generateDeb) { 40 | this.generateDeb = generateDeb; 41 | } 42 | 43 | public boolean isGenerateRpm() { 44 | return generateRpm; 45 | } 46 | 47 | public void setGenerateRpm(boolean generateRpm) { 48 | this.generateRpm = generateRpm; 49 | } 50 | 51 | public boolean isGenerateAppImage() { 52 | return generateAppImage; 53 | } 54 | 55 | public void setGenerateAppImage(boolean generateAppImage) { 56 | this.generateAppImage = generateAppImage; 57 | } 58 | 59 | public File getPngFile() { 60 | return pngFile; 61 | } 62 | 63 | public void setPngFile(File pngFile) { 64 | this.pngFile = pngFile; 65 | } 66 | 67 | public boolean isWrapJar() { 68 | return wrapJar; 69 | } 70 | 71 | public void setWrapJar(boolean wrapJar) { 72 | this.wrapJar = wrapJar; 73 | } 74 | 75 | public String getInstallationPath() { 76 | return installationPath; 77 | } 78 | 79 | public void setInstallationPath(String installationPath) { 80 | this.installationPath = installationPath; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return "LinuxConfig [categories=" + categories + ", generateDeb=" + generateDeb + ", generateRpm=" + generateRpm 86 | + ", generateAppImage=" + generateAppImage + ", pngFile=" + pngFile + ", wrapJar=" + wrapJar 87 | + ", installationPath=" + installationPath + "]"; 88 | } 89 | 90 | /** 91 | * Tests GNU/Linux specific config and set defaults if not specified 92 | * 93 | * @param packager Packager 94 | */ 95 | public void setDefaults(Packager packager) { 96 | this.setCategories((categories == null || categories.isEmpty()) ? Collections.singletonList("Utility") : categories); 97 | this.setInstallationPath(ObjectUtils.defaultIfNull(installationPath, "/opt")); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/MacStartup.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | public enum MacStartup { 4 | UNIVERSAL, 5 | X86_64, 6 | ARM64, 7 | SCRIPT 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Manifest.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * JAR manifest configuration 11 | */ 12 | public class Manifest implements Serializable { 13 | private static final long serialVersionUID = -7271763575775465174L; 14 | 15 | private Map additionalEntries = new HashMap<>(); 16 | private List sections = new ArrayList<>(); 17 | 18 | public Map getAdditionalEntries() { 19 | return additionalEntries; 20 | } 21 | 22 | public void setAdditionalEntries(Map additionalEntries) { 23 | this.additionalEntries = additionalEntries; 24 | } 25 | 26 | public List getSections() { 27 | return sections; 28 | } 29 | 30 | public void setSections(List sections) { 31 | this.sections = sections; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Manifest [additionalEntries=" + additionalEntries + ", sections=" + sections + "]"; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/ManifestSection.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * Manifest section 9 | */ 10 | public class ManifestSection implements Serializable { 11 | private static final long serialVersionUID = 118641813298011799L; 12 | 13 | private String name; 14 | private Map entries = new HashMap<>(); 15 | 16 | public ManifestSection() { 17 | super(); 18 | } 19 | 20 | public ManifestSection(String name, Map entries) { 21 | super(); 22 | this.name = name; 23 | this.entries = entries; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public Map getEntries() { 35 | return entries; 36 | } 37 | 38 | public void setEntries(Map entries) { 39 | this.entries = entries; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "ManifestSection [name=" + name + ", entries=" + entries + "]"; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Platform.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | 5 | /** 6 | * JavaPackager target platform 7 | */ 8 | public enum Platform { 9 | auto, 10 | linux, 11 | mac, 12 | windows; 13 | 14 | public boolean isCurrentPlatform() { 15 | if (this == auto) return true; 16 | return this == getCurrentPlatform(); 17 | } 18 | 19 | public static Platform getCurrentPlatform() { 20 | if (SystemUtils.IS_OS_WINDOWS) return windows; 21 | if (SystemUtils.IS_OS_LINUX) return linux; 22 | if (SystemUtils.IS_OS_MAC) return mac; 23 | return null; 24 | } 25 | 26 | public static Platform getPlatform(String platformString) { 27 | switch (platformString.toLowerCase()) { 28 | case "linux": return linux; 29 | case "darwin": return mac; 30 | case "windows": return windows; 31 | default: throw new IllegalArgumentException("Unknown platform " + platformString); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Registry.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Windows Registry entries to be created when installing using Setup 9 | */ 10 | public class Registry implements Serializable { 11 | private static final long serialVersionUID = 8310081277297116023L; 12 | 13 | private List entries = new ArrayList<>(); 14 | 15 | public Registry() { 16 | super(); 17 | } 18 | 19 | public Registry(List entries) { 20 | super(); 21 | this.entries = entries; 22 | } 23 | 24 | public List getEntries() { 25 | return entries; 26 | } 27 | 28 | public void setEntries(List entries) { 29 | this.entries = entries; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Registry [entries=" + entries + "]"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/RegistryEntry.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Windows Registry entry 7 | */ 8 | public class RegistryEntry implements Serializable { 9 | private static final long serialVersionUID = 447936480111873679L; 10 | 11 | /** 12 | * Windows registry key: HKCU, HKLM, ... 13 | */ 14 | private String key; 15 | 16 | /** 17 | * Windows Registry value name 18 | */ 19 | private String valueName; 20 | 21 | /** 22 | * Windows Registry value type 23 | */ 24 | private ValueType valueType = ValueType.REG_SZ; 25 | 26 | /** 27 | * Windows Registry value data 28 | */ 29 | private String valueData = ""; 30 | 31 | public RegistryEntry() { 32 | super(); 33 | } 34 | 35 | public RegistryEntry(String key, String valueName, ValueType valueType, String valueData) { 36 | super(); 37 | this.key = key; 38 | this.valueName = valueName; 39 | this.valueType = valueType; 40 | this.valueData = valueData; 41 | } 42 | 43 | public String getKey() { 44 | return key; 45 | } 46 | 47 | public void setKey(String key) { 48 | this.key = key; 49 | } 50 | 51 | public String getValueName() { 52 | return valueName; 53 | } 54 | 55 | public void setValueName(String valueName) { 56 | this.valueName = valueName; 57 | } 58 | 59 | public ValueType getValueType() { 60 | return valueType; 61 | } 62 | 63 | public void setValueType(ValueType valueType) { 64 | this.valueType = valueType; 65 | } 66 | 67 | public String getValueData() { 68 | return valueData; 69 | } 70 | 71 | public void setValueData(String valueData) { 72 | this.valueData = valueData; 73 | } 74 | 75 | public String getRoot() { 76 | return key.split(":")[0]; 77 | } 78 | 79 | public String getSubkey() { 80 | String subkey = key.split(":")[1]; 81 | return subkey.startsWith("/") ? subkey.substring(1) : subkey; 82 | } 83 | 84 | /** 85 | * Returns value type as Inno Setup expects 86 | * https://jrsoftware.org/ishelp/index.php?topic=registrysection 87 | * @return Value type converted to IS format 88 | */ 89 | public String getValueTypeAsInnoSetupString() { 90 | switch(valueType) { 91 | case REG_BINARY: return "binary"; 92 | case REG_DWORD: return "dword"; 93 | case REG_EXPAND_SZ: return "expandsz"; 94 | case REG_MULTI_SZ: return "multisz"; 95 | case REG_QWORD: return "qword"; 96 | case REG_SZ: return "string"; 97 | default: return "none"; 98 | } 99 | } 100 | 101 | /** 102 | * Returns value type as WIX Toolset expects 103 | * https://wixtoolset.org/documentation/manual/v3/xsd/wix/registryvalue.html 104 | */ 105 | public String getValueTypeAsWIXToolsetString() { 106 | switch(valueType) { 107 | case REG_BINARY: return "binary"; 108 | case REG_DWORD: return "integer"; 109 | case REG_EXPAND_SZ: return "expandable"; 110 | case REG_MULTI_SZ: return "multiString"; 111 | case REG_QWORD: return "integer"; 112 | case REG_SZ: return "string"; 113 | default: return "none"; 114 | } 115 | } 116 | 117 | @Override 118 | public String toString() { 119 | return "RegistryEntry [key=" + key + ", valueName=" + valueName + ", valueType=" + valueType + ", valueData=" 120 | + valueData + "]"; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Scripts.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | 6 | public class Scripts implements Serializable { 7 | private static final long serialVersionUID = 5665412825491635461L; 8 | 9 | private File bootstrap; 10 | private File preInstall; 11 | private File postInstall; 12 | 13 | public File getBootstrap() { 14 | return bootstrap; 15 | } 16 | 17 | public void setBootstrap(File bootstrap) { 18 | this.bootstrap = bootstrap; 19 | } 20 | 21 | public File getPreInstall() { 22 | return preInstall; 23 | } 24 | 25 | public void setPreInstall(File preInstall) { 26 | this.preInstall = preInstall; 27 | } 28 | 29 | public File getPostInstall() { 30 | return postInstall; 31 | } 32 | 33 | public void setPostInstall(File postInstall) { 34 | this.postInstall = postInstall; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "Scripts [bootstrap=" + bootstrap + ", preInstall=" + preInstall + ", postInstall=" + postInstall + "]"; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/SetupMode.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | /** 4 | * Windows Setup mode 5 | */ 6 | public enum SetupMode { 7 | installForAllUsers, 8 | installForCurrentUser, 9 | askTheUser 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/Template.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | public class Template { 4 | 5 | private String name; 6 | private boolean bom = false; 7 | 8 | public Template() {} 9 | 10 | public Template(String name, boolean bom) { 11 | this.name = name; 12 | this.bom = bom; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public boolean isBom() { 24 | return bom; 25 | } 26 | 27 | public void setBom(boolean bom) { 28 | this.bom = bom; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "Template [name=" + name + ", bom=" + bom + "]"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/ValueType.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | /** 4 | * Value type of Windows Registry entries. 5 | */ 6 | public enum ValueType { 7 | REG_SZ, 8 | REG_EXPAND_SZ, 9 | REG_MULTI_SZ, 10 | REG_DWORD, 11 | REG_QWORD, 12 | REG_BINARY 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/WindowsExeCreationTool.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | public enum WindowsExeCreationTool { 4 | launch4j, 5 | why, 6 | winrun4j; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/model/WindowsSigning.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.model; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | /** 9 | * Info needed for signing EXEs on Windows 10 | */ 11 | public class WindowsSigning implements Serializable { 12 | 13 | private static final long serialVersionUID = 2559089741502151307L; 14 | 15 | private String storetype; 16 | private File keystore; 17 | private File certfile; 18 | private File keyfile; 19 | private String storepass; 20 | private String alias; 21 | private String keypass; 22 | private String alg; 23 | 24 | public String getStoretype() { 25 | return storetype; 26 | } 27 | 28 | public void setStoretype(String storetype) { 29 | this.storetype = storetype; 30 | } 31 | 32 | public File getKeystore() { 33 | return keystore; 34 | } 35 | 36 | public void setKeystore(File keystore) { 37 | this.keystore = keystore; 38 | } 39 | 40 | public File getCertfile() { 41 | return certfile; 42 | } 43 | 44 | public void setCertfile(File certfile) { 45 | this.certfile = certfile; 46 | } 47 | 48 | public File getKeyfile() { 49 | return keyfile; 50 | } 51 | 52 | public void setKeyfile(File keyfile) { 53 | this.keyfile = keyfile; 54 | } 55 | 56 | public String getStorepass() { 57 | return storepass; 58 | } 59 | 60 | public void setStorepass(String storepass) { 61 | this.storepass = storepass; 62 | } 63 | 64 | public String getAlias() { 65 | return alias; 66 | } 67 | 68 | public void setAlias(String alias) { 69 | this.alias = alias; 70 | } 71 | 72 | public String getKeypass() { 73 | return keypass; 74 | } 75 | 76 | public void setKeypass(String keypass) { 77 | this.keypass = keypass; 78 | } 79 | 80 | public String getAlg() { 81 | return alg; 82 | } 83 | 84 | public void setAlg(String alg) { 85 | this.alg = alg; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | String keypass = this.keypass != null ? StringUtils.repeat("*", this.keypass.length()) : ""; 91 | String storepass = this.storepass != null ? StringUtils.repeat("*", this.storepass.length()) : ""; 92 | return "WindowsSigning [storetype=" + storetype + ", keystore=" + keystore + ", certfile=" + certfile 93 | + ", keyfile=" + keyfile + ", storepass=" + storepass + ", alias=" + alias + ", keypass=" + keypass + ", alg=" + alg 94 | + "]"; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/AbstractCreateWindowsExe.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | import io.github.fvarrui.javapackager.model.WindowsExeCreationTool; 7 | import io.github.fvarrui.javapackager.utils.FileUtils; 8 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 9 | 10 | public abstract class AbstractCreateWindowsExe extends ArtifactGenerator { 11 | 12 | private final File outputFolder; 13 | private File genericManifest; 14 | private File genericIcon; 15 | private File genericJar; 16 | private File genericExe; 17 | 18 | public AbstractCreateWindowsExe(WindowsExeCreationTool tool) { 19 | super(tool.toString()); 20 | this.outputFolder = new File(Context.getContext().getBuildDir(), tool.toString()); 21 | } 22 | 23 | public File getGenericManifest() { 24 | return genericManifest; 25 | } 26 | 27 | public void setGenericManifest(File genericManifest) { 28 | this.genericManifest = genericManifest; 29 | } 30 | 31 | public File getGenericIcon() { 32 | return genericIcon; 33 | } 34 | 35 | public void setGenericIcon(File genericIcon) { 36 | this.genericIcon = genericIcon; 37 | } 38 | 39 | public File getGenericJar() { 40 | return genericJar; 41 | } 42 | 43 | public void setGenericJar(File genericJar) { 44 | this.genericJar = genericJar; 45 | } 46 | 47 | public File getGenericExe() { 48 | return genericExe; 49 | } 50 | 51 | public void setGenericExe(File genericExe) { 52 | this.genericExe = genericExe; 53 | } 54 | 55 | public File getOutputFolder() { 56 | return outputFolder; 57 | } 58 | 59 | /** 60 | * Renames assets required for exe generation to avoid unsupported characters 61 | * (chinese, e.g.) 62 | * 63 | * @param packager Windows packager 64 | * @throws Exception Something went wrong 65 | */ 66 | protected void createAssets(WindowsPackager packager) throws Exception { 67 | 68 | File manifestFile = packager.getManifestFile(); 69 | File iconFile = packager.getIconFile(); 70 | File jarFile = packager.getJarFile(); 71 | 72 | FileUtils.mkdir(outputFolder); 73 | 74 | genericManifest = new File(outputFolder, "app.exe.manifest"); 75 | genericIcon = new File(outputFolder, "app.ico"); 76 | genericJar = new File(outputFolder, "app.jar"); 77 | genericExe = new File(outputFolder, "app.exe"); 78 | 79 | FileUtils.copyFileToFile(manifestFile, genericManifest); 80 | FileUtils.copyFileToFile(iconFile, genericIcon); 81 | FileUtils.copyFileToFile(jarFile, genericJar); 82 | 83 | } 84 | 85 | /** 86 | * Creates bootstrap script if needed 87 | * @param packager 88 | * @return 89 | * @throws Exception 90 | */ 91 | protected File createBootstrapScript(WindowsPackager packager) throws Exception { 92 | File executable = packager.getExecutable(); 93 | 94 | if (FileUtils.exists(packager.getScripts().getBootstrap())) { 95 | 96 | // generates startup VBS script file 97 | File vbsFile = new File(packager.getAppFolder(), packager.getName() + ".vbs"); 98 | VelocityUtils.render(Platform.windows + "/startup.vbs.vtl", vbsFile, packager); 99 | executable = vbsFile; 100 | 101 | } 102 | 103 | return executable; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/ArtifactGenerator.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.utils.Logger; 6 | 7 | 8 | /** 9 | * Artifact generation base class 10 | */ 11 | public abstract class ArtifactGenerator { 12 | 13 | private String artifactName; 14 | 15 | public ArtifactGenerator() { 16 | super(); 17 | } 18 | 19 | public ArtifactGenerator(String artifactName) { 20 | super(); 21 | this.artifactName = artifactName; 22 | } 23 | 24 | public boolean skip(T packager) { 25 | return false; 26 | } 27 | 28 | public String getArtifactName() { 29 | return artifactName; 30 | } 31 | 32 | public void setArtifactName(String artifactName) { 33 | this.artifactName = artifactName; 34 | } 35 | 36 | protected abstract File doApply(T packager) throws Exception; 37 | 38 | @SuppressWarnings("unchecked") 39 | public File apply(Packager packager) throws Exception { 40 | if (skip((T)packager)) { 41 | Logger.warn(getArtifactName() + " artifact generation skipped!"); 42 | return null; 43 | } 44 | return doApply((T)packager); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/Context.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.apache.commons.collections4.map.HashedMap; 9 | 10 | import io.github.fvarrui.javapackager.gradle.GradleContext; 11 | import io.github.fvarrui.javapackager.maven.MavenContext; 12 | import io.github.fvarrui.javapackager.model.Platform; 13 | 14 | /** 15 | * Building-tool context 16 | */ 17 | public abstract class Context { 18 | 19 | public Context() { 20 | super(); 21 | 22 | // building tool independent generators 23 | getInstallerGenerators(Platform.linux).add(new GenerateDeb()); 24 | getInstallerGenerators(Platform.linux).add(new GenerateRpm()); 25 | getInstallerGenerators(Platform.linux).add(new GenerateAppImage()); 26 | getInstallerGenerators(Platform.mac).add(new GenerateDmg()); 27 | getInstallerGenerators(Platform.mac).add(new GeneratePkg()); 28 | getInstallerGenerators(Platform.windows).add(new GenerateSetup()); 29 | getInstallerGenerators(Platform.windows).add(new GenerateMsm()); 30 | getInstallerGenerators(Platform.windows).add(new GenerateMsi()); 31 | 32 | } 33 | 34 | // common properties 35 | 36 | public abstract File getRootDir(); 37 | public abstract File getBuildDir(); 38 | public abstract T getLogger(); 39 | 40 | // platform independent functions 41 | 42 | public abstract File createRunnableJar(Packager packager) throws Exception; 43 | public abstract File copyDependencies(Packager packager) throws Exception; 44 | public abstract File createTarball(Packager packager) throws Exception; 45 | public abstract File createZipball(Packager packager) throws Exception; 46 | public abstract File resolveLicense(Packager packager) throws Exception; 47 | public abstract File createWindowsExe(WindowsPackager packager) throws Exception; 48 | 49 | // installer producers 50 | 51 | private Map>> installerGeneratorsMap = new HashedMap<>(); 52 | 53 | public List> getInstallerGenerators(Platform platform) { 54 | List> platformInstallers = installerGeneratorsMap.get(platform); 55 | if (platformInstallers == null) { 56 | platformInstallers = new ArrayList<>(); 57 | installerGeneratorsMap.put(platform, platformInstallers); 58 | } 59 | return platformInstallers; 60 | } 61 | 62 | // static context 63 | 64 | private static Context context; 65 | 66 | public static Context getContext() { 67 | return context; 68 | } 69 | 70 | public static void setContext(Context context) { 71 | Context.context = context; 72 | } 73 | 74 | public static boolean isMaven() { 75 | return context instanceof MavenContext; 76 | } 77 | 78 | public static boolean isGradle() { 79 | return context instanceof GradleContext; 80 | } 81 | 82 | public static MavenContext getMavenContext() { 83 | return (MavenContext) context; 84 | } 85 | 86 | public static GradleContext getGradleContext() { 87 | return (GradleContext) context; 88 | } 89 | 90 | public File getDefaultToolchain() { 91 | if (System.getenv("JAVA_HOME") != null) { 92 | return new File(System.getenv("JAVA_HOME")); // Use JAVA_HOME as fallback 93 | } 94 | return new File(System.getProperty("java.home")); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWhy.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | import io.github.fvarrui.javapackager.model.WindowsConfig; 7 | import io.github.fvarrui.javapackager.model.WindowsExeCreationTool; 8 | import io.github.fvarrui.javapackager.utils.FileUtils; 9 | import io.github.fvarrui.javapackager.utils.Logger; 10 | import io.github.fvarrui.javapackager.utils.RcEdit; 11 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 12 | import net.jsign.WindowsSigner; 13 | 14 | /** 15 | * Creates Windows executable with WinRun4j 16 | */ 17 | public class CreateWindowsExeWhy extends AbstractCreateWindowsExe { 18 | 19 | public CreateWindowsExeWhy() { 20 | super(WindowsExeCreationTool.why); 21 | } 22 | 23 | @Override 24 | public boolean skip(WindowsPackager packager) { 25 | 26 | if (!packager.getPlatform().isCurrentPlatform()) { 27 | Logger.error(getArtifactName() + " cannot be generated with Why due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | @Override 35 | protected File doApply(WindowsPackager packager) throws Exception { 36 | 37 | File executable = packager.getExecutable(); 38 | File manifestFile = packager.getManifestFile(); 39 | File iconFile = packager.getIconFile(); 40 | File appFolder = packager.getAppFolder(); 41 | File jarFile = packager.getJarFile(); 42 | WindowsConfig winConfig = packager.getWinConfig(); 43 | 44 | if (winConfig.isWrapJar()) { 45 | Logger.warn("'wrapJar' property ignored when building EXE with " + getArtifactName()); 46 | } 47 | 48 | createAssets(packager); 49 | 50 | // creates generic manifest 51 | FileUtils.copyFileToFile(manifestFile, getGenericManifest()); 52 | 53 | // creates generic manifest 54 | FileUtils.copyFileToFile(iconFile, getGenericIcon()); 55 | 56 | // creates generic exe 57 | FileUtils.copyResourceToFile("/windows/JavaLauncher.exe", getGenericExe(), packager.getAssetsDir()); 58 | 59 | // generates ini file 60 | File genericIni = new File(getOutputFolder(), "launcher.ini"); 61 | VelocityUtils.render("windows/why-ini.vtl", genericIni, packager); 62 | Logger.info("INI file generated in " + genericIni.getAbsolutePath() + "!"); 63 | 64 | // set exe metadata with rcedit 65 | RcEdit rcedit = new RcEdit(getOutputFolder()); 66 | rcedit.setIcon(getGenericExe(), getGenericIcon()); 67 | rcedit.setManifest(getGenericExe(), getGenericManifest()); 68 | rcedit.setFileVersion(getGenericExe(), winConfig.getFileVersion()); 69 | rcedit.setProductVersion(getGenericExe(), winConfig.getProductVersion()); 70 | rcedit.setVersionString(getGenericExe(), "FileDescription", winConfig.getFileDescription()); 71 | rcedit.setVersionString(getGenericExe(), "CompanyName", winConfig.getCompanyName()); 72 | rcedit.setVersionString(getGenericExe(), "InternalName", winConfig.getInternalName()); 73 | rcedit.setVersionString(getGenericExe(), "OriginalFilename", winConfig.getOriginalFilename()); 74 | rcedit.setVersionString(getGenericExe(), "ProductName", winConfig.getProductName()); 75 | 76 | // copies JAR to app folder 77 | FileUtils.copyFileToFolder(jarFile, appFolder); 78 | 79 | // copies ini file to app folder 80 | FileUtils.copyFileToFolder(genericIni, appFolder); 81 | 82 | // copies exe file to app folder with apps name 83 | FileUtils.copyFileToFile(getGenericExe(), executable); 84 | 85 | return createBootstrapScript(packager); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWinRun4j.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | import java.nio.file.Path; 5 | import java.util.Arrays; 6 | import java.util.Optional; 7 | 8 | import net.jsign.WindowsSigner; 9 | import org.apache.commons.lang3.StringUtils; 10 | 11 | import io.github.fvarrui.javapackager.model.Arch; 12 | import io.github.fvarrui.javapackager.model.Platform; 13 | import io.github.fvarrui.javapackager.model.WindowsConfig; 14 | import io.github.fvarrui.javapackager.model.WindowsExeCreationTool; 15 | import io.github.fvarrui.javapackager.utils.FileUtils; 16 | import io.github.fvarrui.javapackager.utils.JDKUtils; 17 | import io.github.fvarrui.javapackager.utils.Logger; 18 | import io.github.fvarrui.javapackager.utils.RcEdit; 19 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 20 | 21 | /** 22 | * Creates Windows executable with WinRun4j 23 | */ 24 | public class CreateWindowsExeWinRun4j extends AbstractCreateWindowsExe { 25 | 26 | private static final String [] JVM_DLL_PATHS = { 27 | "bin/client/jvm.dll", 28 | "bin/server/jvm.dll" 29 | }; 30 | 31 | public CreateWindowsExeWinRun4j() { 32 | super(WindowsExeCreationTool.winrun4j); 33 | } 34 | 35 | @Override 36 | public boolean skip(WindowsPackager packager) { 37 | 38 | if (!packager.getPlatform().isCurrentPlatform()) { 39 | Logger.error(getArtifactName() + " cannot be generated with WinRun4J due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | 46 | @Override 47 | protected File doApply(WindowsPackager packager) throws Exception { 48 | 49 | String name = packager.getName(); 50 | File executable = packager.getExecutable(); 51 | File jarFile = packager.getJarFile(); 52 | File manifestFile = packager.getManifestFile(); 53 | File iconFile = packager.getIconFile(); 54 | File appFolder = packager.getAppFolder(); 55 | File jreDestinationFolder = packager.getJreDestinationFolder(); 56 | boolean bundleJre = packager.getBundleJre(); 57 | String vmLocation = packager.getWinConfig().getVmLocation(); 58 | WindowsConfig winConfig = packager.getWinConfig(); 59 | Arch arch = packager.getArch(); 60 | 61 | if (winConfig.isWrapJar()) { 62 | Logger.warn("'wrapJar' property ignored when building EXE with " + getArtifactName()); 63 | } 64 | 65 | createAssets(packager); 66 | 67 | // creates generic manifest 68 | FileUtils.copyFileToFile(manifestFile, getGenericManifest()); 69 | 70 | // creates generic manifest 71 | FileUtils.copyFileToFile(iconFile, getGenericIcon()); 72 | 73 | // checks if target architecture matches JRE arch 74 | if (bundleJre && !JDKUtils.isValidJRE(Platform.windows, arch, packager.getJreDestinationFolder())) { 75 | throw new Exception("Bundled JRE must match " + Platform.windows + " " + arch); 76 | } 77 | 78 | // creates generic exe 79 | if (arch == Arch.x86) { 80 | FileUtils.copyResourceToFile("/windows/WinRun4J.exe", getGenericExe()); 81 | } else { 82 | FileUtils.copyResourceToFile("/windows/WinRun4J64.exe", getGenericExe()); 83 | } 84 | 85 | // uses vmLocation only if a JRE is bundled 86 | if (bundleJre) { 87 | 88 | // checks if vmLocation property is specified in winConfig 89 | if (!StringUtils.isBlank(vmLocation)) { 90 | 91 | // checks if specified vmLocation exists 92 | if (!new File(jreDestinationFolder, vmLocation).exists()) { 93 | throw new Exception("VM location '" + vmLocation + "' does not exist"); 94 | } 95 | 96 | vmLocation = vmLocation.replaceAll("/", "\\\\"); 97 | 98 | } else { 99 | 100 | // searchs for a valid jvm.dll file in JRE 101 | Optional jvmDllFile = Arrays.asList(JVM_DLL_PATHS) 102 | .stream() 103 | .map(path -> new File(jreDestinationFolder, path)) 104 | .filter(file -> file.exists()) 105 | .findFirst(); 106 | 107 | // checks if found jvm.dll 108 | if (!jvmDllFile.isPresent()) { 109 | throw new Exception("jvm.dll not found!"); 110 | } 111 | 112 | // relativize jvm.dll path to JRE, in order to use it a "vm.location" 113 | Path jreDestinationPath = jreDestinationFolder.toPath(); 114 | Path jvmDllPath = jvmDllFile.get().toPath(); 115 | vmLocation = jreDestinationPath.relativize(jvmDllPath).toString(); 116 | 117 | } 118 | 119 | // sets vmLocation in winConfig, so it will be used when rendering INI file 120 | packager.getWinConfig().setVmLocation(vmLocation); 121 | 122 | Logger.info("Using 'vmLocation=" + vmLocation + "'!"); 123 | 124 | } 125 | 126 | // set exe metadata with rcedit 127 | RcEdit rcedit = new RcEdit(getOutputFolder()); 128 | rcedit.setIcon(getGenericExe(), getGenericIcon()); 129 | rcedit.setManifest(getGenericExe(), getGenericManifest()); 130 | rcedit.setFileVersion(getGenericExe(), winConfig.getFileVersion()); 131 | rcedit.setProductVersion(getGenericExe(), winConfig.getProductVersion()); 132 | rcedit.setVersionString(getGenericExe(), "FileDescription", winConfig.getFileDescription()); 133 | rcedit.setVersionString(getGenericExe(), "CompanyName", winConfig.getCompanyName()); 134 | rcedit.setVersionString(getGenericExe(), "InternalName", winConfig.getInternalName()); 135 | rcedit.setVersionString(getGenericExe(), "OriginalFilename", winConfig.getOriginalFilename()); 136 | rcedit.setVersionString(getGenericExe(), "ProductName", winConfig.getProductName()); 137 | 138 | // copies JAR to libs folder 139 | FileUtils.copyFileToFolder(jarFile, appFolder); 140 | 141 | // generates ini file 142 | File genericIni = new File(getOutputFolder(), "app.ini"); 143 | VelocityUtils.render("windows/ini.vtl", genericIni, packager); 144 | Logger.info("INI file generated in " + genericIni.getAbsolutePath() + "!"); 145 | 146 | // copies ini file to app folder 147 | File iniFile = new File(appFolder, name + ".ini"); 148 | FileUtils.copyFileToFile(genericIni, iniFile); 149 | 150 | // copies exe file to app folder with apps name 151 | FileUtils.copyFileToFile(getGenericExe(), executable); 152 | 153 | return createBootstrapScript(packager); 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateAppImage.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import io.github.fvarrui.javapackager.model.Platform; 4 | import io.github.fvarrui.javapackager.utils.CommandUtils; 5 | import io.github.fvarrui.javapackager.utils.FileUtils; 6 | import io.github.fvarrui.javapackager.utils.Logger; 7 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 8 | import org.apache.commons.lang3.SystemUtils; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | public class GenerateAppImage extends ArtifactGenerator { 14 | 15 | private static final int IMAGETOOL_VERSION = 13; 16 | private static final String IMAGETOOL_URL = "https://github.com/AppImage/AppImageKit/releases/download/" + IMAGETOOL_VERSION + "/appimagetool-%s.AppImage"; 17 | 18 | public GenerateAppImage() { 19 | super("AppImage"); 20 | } 21 | 22 | @Override 23 | public boolean skip(LinuxPackager packager) { 24 | 25 | if (!packager.getLinuxConfig().isGenerateAppImage()) { 26 | return true; 27 | } 28 | 29 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 30 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 31 | return true; 32 | } 33 | 34 | return false; 35 | 36 | } 37 | 38 | @Override 39 | protected File doApply(LinuxPackager packager) throws Exception { 40 | 41 | File appFolder = packager.getAppFolder(); 42 | File outputFolder = packager.getOutputDirectory(); 43 | String name = packager.getName(); 44 | File executable = packager.getExecutable(); 45 | File assetsFolder = packager.getAssetsFolder(); 46 | File iconFile = packager.getIconFile(); 47 | 48 | // output AppImage file 49 | File appImage = new File(outputFolder, name + ".AppImage"); 50 | 51 | // AppDir folder 52 | File appDir = new File(assetsFolder, "AppDir"); 53 | 54 | // gets/downloads AppImage tool 55 | Logger.info("Getting appimagetool..."); 56 | File appImageTool = getAppImageTool(packager); 57 | Logger.info("App image tool found! " + appImageTool); 58 | 59 | // copies app folder to AppDir/usr/bin 60 | FileUtils.copyFolderContentToFolder(appFolder, new File(appDir, "usr/bin")); 61 | 62 | // generates AppImage desktop file from velocity template 63 | File desktopFile = new File(appDir, name + ".desktop"); 64 | VelocityUtils.render("linux/desktop-appimage.vtl", desktopFile, packager); 65 | Logger.info("Desktop file rendered in " + desktopFile.getAbsolutePath()); 66 | 67 | // creates AppRun symlink to startup script 68 | Logger.info("Creating AppRun symlink to startup script..."); 69 | File appRun = new File(appDir, "AppRun"); 70 | FileUtils.createSymlink(appRun, new File("usr/bin", executable.getName())); 71 | 72 | // creates AppRun symlink to startup script 73 | Logger.info("Copying icon to AppDir ..."); 74 | FileUtils.copyFileToFolder(iconFile, appDir); 75 | 76 | // runs appimagetool on appFolder 77 | Logger.info("Running appimagetool on " + appFolder); 78 | CommandUtils.execute( 79 | appImageTool, 80 | "--appimage-extract-and-run", 81 | appDir, 82 | appImage 83 | ); 84 | 85 | Logger.info("Setting execution permissions to " + appImage); 86 | appImage.setExecutable(true); 87 | 88 | return appImage; 89 | } 90 | 91 | private File getAppImageTool(LinuxPackager packager) throws Exception { 92 | File assetsFolder = packager.getAssetsFolder(); 93 | File appImageTool = new File(assetsFolder, "appimagetool"); 94 | if (!appImageTool.exists()) { 95 | String imageToolUrl = String.format(IMAGETOOL_URL, getOSArch()); 96 | try { 97 | FileUtils.downloadFromUrl(imageToolUrl, appImageTool); 98 | } catch (IOException e) { 99 | throw new Exception("An error occurred while downloading appimagetool from " + imageToolUrl + " for " + getOSArch() + "! It may be a network problem or the url " + imageToolUrl + " is not valid!", e); 100 | } 101 | appImageTool.setExecutable(true); 102 | } 103 | return appImageTool; 104 | } 105 | 106 | private String getOSArch() { 107 | switch (SystemUtils.OS_ARCH) { 108 | case "amd64": 109 | return "x86_64"; 110 | case "x86": 111 | case "i386": 112 | return "i686"; 113 | } 114 | return SystemUtils.OS_ARCH; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDmg.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import static io.github.fvarrui.javapackager.utils.CommandUtils.execute; 4 | import static org.apache.commons.lang3.StringUtils.defaultIfBlank; 5 | 6 | import java.io.File; 7 | import java.util.Arrays; 8 | import java.util.Optional; 9 | 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | import io.github.fvarrui.javapackager.model.MacConfig; 13 | import io.github.fvarrui.javapackager.model.Platform; 14 | import io.github.fvarrui.javapackager.utils.FileUtils; 15 | import io.github.fvarrui.javapackager.utils.Logger; 16 | import io.github.fvarrui.javapackager.utils.ThreadUtils; 17 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 18 | 19 | /** 20 | * Creates a DMG image file including all app folder's content only for MacOS so 21 | * app could be easily distributed 22 | */ 23 | public class GenerateDmg extends ArtifactGenerator { 24 | 25 | public GenerateDmg() { 26 | super("DMG image"); 27 | } 28 | 29 | @Override 30 | public boolean skip(MacPackager packager) { 31 | 32 | if (!packager.getMacConfig().isGenerateDmg()) { 33 | return true; 34 | } 35 | 36 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 37 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | @Override 45 | protected File doApply(MacPackager packager) throws Exception { 46 | 47 | File appFolder = packager.getAppFolder(); 48 | File assetsFolder = packager.getAssetsFolder(); 49 | String name = packager.getName(); 50 | File outputDirectory = packager.getOutputDirectory(); 51 | File iconFile = packager.getIconFile(); 52 | String version = packager.getVersion(); 53 | MacConfig macConfig = packager.getMacConfig(); 54 | 55 | // sets volume name if blank 56 | String volumeName = defaultIfBlank(macConfig.getVolumeName(), name); 57 | 58 | // removes whitespaces from volume name 59 | if (StringUtils.containsWhitespace(volumeName)) { 60 | volumeName = volumeName.replaceAll(" ", ""); 61 | Logger.warn("Whitespaces has been removed from volume name: " + volumeName); 62 | } 63 | 64 | // final dmg file 65 | File dmgFile = new File(outputDirectory, name + "_" + version + ".dmg"); 66 | 67 | // temp dmg file 68 | File tempDmgFile = new File(assetsFolder, dmgFile.getName()); 69 | 70 | // mount dir 71 | File mountFolder = new File("/Volumes/" + volumeName); 72 | 73 | // copies background file 74 | Logger.info("Copying background image"); 75 | File backgroundFolder = FileUtils.mkdir(appFolder, ".background"); 76 | File backgroundFile = new File(backgroundFolder, "background.png"); 77 | if (macConfig.getBackgroundImage() != null) 78 | FileUtils.copyFileToFile(macConfig.getBackgroundImage(), backgroundFile); 79 | else 80 | FileUtils.copyResourceToFile("/mac/background.png", backgroundFile); 81 | 82 | // copies volume icon 83 | Logger.info("Copying icon file: " + iconFile.getAbsolutePath()); 84 | File volumeIcon = (macConfig.getVolumeIcon() != null) ? macConfig.getVolumeIcon() : iconFile; 85 | FileUtils.copyFileToFile(volumeIcon, new File(appFolder, ".VolumeIcon.icns")); 86 | 87 | // creates image 88 | Logger.info("Creating image: " + tempDmgFile.getAbsolutePath()); 89 | String osArchitecture = System.getProperty("os.arch"); 90 | boolean isAarch64 = osArchitecture.equalsIgnoreCase("aarch64"); 91 | String fileSystem = isAarch64 ? "APFS" : "HFS+"; 92 | Logger.warn(osArchitecture + " architecture detected. Using " + fileSystem + " filesystem"); 93 | execute("hdiutil", "create", "-srcfolder", appFolder, "-volname", volumeName, "-ov", "-fs", fileSystem, "-format", "UDRW", tempDmgFile); 94 | 95 | if (mountFolder.exists()) { 96 | Logger.info("Unmounting volume: " + mountFolder); 97 | execute("hdiutil", "detach", mountFolder); 98 | } 99 | 100 | // mounts image 101 | Logger.info("Mounting image: " + tempDmgFile.getAbsolutePath()); 102 | String result = execute("hdiutil", "attach", "-readwrite", "-noverify", "-noautoopen", tempDmgFile); 103 | Optional optDeviceName = Arrays.stream(result.split("\n")) 104 | .filter(s -> s.contains(mountFolder.getAbsolutePath())) 105 | .map(StringUtils::normalizeSpace) 106 | .map(s -> s.split(" ")[0]) 107 | .findFirst(); 108 | optDeviceName.ifPresent(deviceName -> Logger.info("- Device name: " + deviceName)); 109 | 110 | // pause to prevent occasional "Can't get disk" (-1728) issues 111 | // https://github.com/seltzered/create-dmg/commit/5fe7802917bb85b40c0630b026d33e421db914ea 112 | ThreadUtils.sleep(2000L); 113 | 114 | // creates a symlink to Applications folder 115 | Logger.info("Creating Applications link"); 116 | File targetFolder = new File("/Applications"); 117 | File linkFile = new File(mountFolder, "Applications"); 118 | FileUtils.createSymlink(linkFile, targetFolder); 119 | 120 | // renders applescript 121 | Logger.info("Rendering DMG customization applescript ... "); 122 | File applescriptFile = new File(assetsFolder, "customize-dmg.applescript"); 123 | VelocityUtils.render("/mac/customize-dmg.applescript.vtl", applescriptFile, packager); 124 | Logger.info("Applescript rendered in " + applescriptFile.getAbsolutePath() + "!"); 125 | 126 | // runs applescript 127 | Logger.info("Running applescript"); 128 | execute("/usr/bin/osascript", applescriptFile, volumeName); 129 | 130 | // makes sure it's not world writeable and user readable 131 | Logger.info("Fixing permissions..."); 132 | execute("chmod", "-Rf", "u+r,go-w", mountFolder); 133 | 134 | if (!isAarch64) { 135 | // makes the top window open itself on mount: 136 | Logger.info("Blessing ..."); 137 | try { 138 | execute("bless", "--folder", mountFolder, "--openfolder", mountFolder); } 139 | catch (Exception e){ 140 | Logger.warn("Error blessing " + mountFolder + " due to: " + e.getMessage()); 141 | } 142 | } 143 | 144 | // tells the volume that it has a special file attribute 145 | execute("SetFile", "-a", "C", mountFolder); 146 | 147 | // unmounts 148 | Logger.info("Unmounting volume: " + mountFolder); 149 | execute("hdiutil", "detach", mountFolder); 150 | 151 | // compress image 152 | Logger.info("Compressing disk image..."); 153 | execute("hdiutil", "convert", tempDmgFile, "-ov", "-format", "UDZO", "-imagekey", "zlib-level=9", "-o", dmgFile); 154 | tempDmgFile.delete(); 155 | 156 | // checks if dmg file was created 157 | if (!dmgFile.exists()) { 158 | throw new Exception(getArtifactName() + " generation failed!"); 159 | } 160 | 161 | return dmgFile; 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsi.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import static io.github.fvarrui.javapackager.utils.CommandUtils.execute; 4 | 5 | import java.io.File; 6 | 7 | import io.github.fvarrui.javapackager.model.Platform; 8 | import io.github.fvarrui.javapackager.utils.Logger; 9 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 10 | import io.github.fvarrui.javapackager.utils.XMLUtils; 11 | import net.jsign.WindowsSigner; 12 | 13 | /** 14 | * Creates an MSI file including all app folder's content only for 15 | * Windows so app could be easily distributed 16 | */ 17 | public class GenerateMsi extends ArtifactGenerator { 18 | 19 | public GenerateMsi() { 20 | super("MSI installer"); 21 | } 22 | 23 | @Override 24 | public boolean skip(WindowsPackager packager) { 25 | 26 | if (!packager.getWinConfig().isGenerateMsi()) { 27 | return true; 28 | } 29 | 30 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 31 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 32 | return true; 33 | } 34 | 35 | return false; 36 | } 37 | 38 | @Override 39 | protected File doApply(WindowsPackager packager) throws Exception { 40 | 41 | File msmFile = new GenerateMsm().doApply(packager); 42 | Logger.info("MSM file generated in " + msmFile); 43 | 44 | File assetsFolder = packager.getAssetsFolder(); 45 | String name = packager.getName(); 46 | File outputDirectory = packager.getOutputDirectory(); 47 | String version = packager.getVersion(); 48 | 49 | // generates WXS file from velocity template 50 | File wxsFile = new File(assetsFolder, name + ".wxs"); 51 | VelocityUtils.render("windows/wxs.vtl", wxsFile, packager); 52 | Logger.info("WXS file generated in " + wxsFile + "!"); 53 | 54 | // prettify wxs 55 | XMLUtils.prettify(wxsFile); 56 | 57 | // candle wxs file 58 | Logger.info("Compiling file " + wxsFile); 59 | File wixobjFile = new File(assetsFolder, name + ".wixobj"); 60 | execute("candle", "-out", wixobjFile, wxsFile); 61 | Logger.info("WIXOBJ file generated in " + wixobjFile + "!"); 62 | 63 | // lighting wxs file 64 | Logger.info("Linking file " + wixobjFile); 65 | File msiFile = new File(outputDirectory, name + "_" + version + ".msi"); 66 | execute("light", "-sw1076", "-spdb", "-out", msiFile, wixobjFile); 67 | 68 | // setup file 69 | if (!msiFile.exists()) { 70 | throw new Exception("MSI installer file generation failed!"); 71 | } 72 | 73 | // sign installer 74 | WindowsSigner.sign(msiFile, packager.getDisplayName(), packager.getUrl(), packager.getWinConfig().getSigning()); 75 | 76 | return msiFile; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsm.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | import io.github.fvarrui.javapackager.utils.CommandUtils; 7 | import io.github.fvarrui.javapackager.utils.Logger; 8 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 9 | import io.github.fvarrui.javapackager.utils.XMLUtils; 10 | 11 | /** 12 | * Creates an MSI file including all app folder's content only for Windows so app 13 | * could be easily distributed 14 | */ 15 | public class GenerateMsm extends ArtifactGenerator { 16 | 17 | public GenerateMsm() { 18 | super("MSI merge module"); 19 | } 20 | 21 | @Override 22 | public boolean skip(WindowsPackager packager) { 23 | 24 | if (!packager.getWinConfig().isGenerateMsm() && !packager.getWinConfig().isGenerateMsi()) { 25 | return true; 26 | } 27 | 28 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 29 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 30 | return true; 31 | } 32 | 33 | return false; 34 | } 35 | 36 | @Override 37 | protected File doApply(WindowsPackager packager) throws Exception { 38 | 39 | if (packager.getMsmFile() != null) { 40 | return packager.getMsmFile(); 41 | } 42 | 43 | File assetsFolder = packager.getAssetsFolder(); 44 | String name = packager.getName(); 45 | File outputDirectory = packager.getOutputDirectory(); 46 | String version = packager.getVersion(); 47 | 48 | // generates WXS file from velocity template 49 | File wxsFile = new File(assetsFolder, name + ".msm.wxs"); 50 | VelocityUtils.render("windows/msm.wxs.vtl", wxsFile, packager); 51 | Logger.info("WXS file generated in " + wxsFile + "!"); 52 | 53 | // prettify wxs 54 | XMLUtils.prettify(wxsFile); 55 | 56 | // candle wxs file 57 | Logger.info("Compiling file " + wxsFile); 58 | File wixobjFile = new File(assetsFolder, name + ".msm.wixobj"); 59 | CommandUtils.execute("candle", "-out", wixobjFile, wxsFile); 60 | Logger.info("WIXOBJ file generated in " + wixobjFile + "!"); 61 | 62 | // lighting wxs file 63 | Logger.info("Linking file " + wixobjFile); 64 | File msmFile = new File(outputDirectory, name + "_" + version + ".msm"); 65 | CommandUtils.execute("light", "-sw1076", "-spdb", "-out", msmFile, wixobjFile); 66 | 67 | // setup file 68 | if (!msmFile.exists()) { 69 | throw new Exception("MSI installer file generation failed!"); 70 | } 71 | 72 | packager.setMsmFile(msmFile); 73 | 74 | return msmFile; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GeneratePkg.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | import io.github.fvarrui.javapackager.utils.CommandUtils; 7 | import io.github.fvarrui.javapackager.utils.Logger; 8 | 9 | /** 10 | * Creates a PKG installer file including all app folder's content only for MacOS so 11 | * app could be easily distributed 12 | */ 13 | public class GeneratePkg extends ArtifactGenerator { 14 | 15 | public GeneratePkg() { 16 | super("PKG installer"); 17 | } 18 | 19 | @Override 20 | public boolean skip(MacPackager packager) { 21 | 22 | if (!packager.getMacConfig().isGeneratePkg()) { 23 | return true; 24 | } 25 | 26 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 27 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | @Override 35 | protected File doApply(MacPackager packager) throws Exception { 36 | 37 | File appFile = packager.getAppFile(); 38 | String name = packager.getName(); 39 | File outputDirectory = packager.getOutputDirectory(); 40 | String version = packager.getVersion(); 41 | 42 | File pkgFile = new File(outputDirectory, name + "_" + version + ".pkg"); 43 | 44 | // invokes pkgbuild command 45 | CommandUtils.execute("pkgbuild", "--install-location", "/Applications", "--component", appFile, pkgFile); 46 | 47 | // checks if pkg file was created 48 | if (!pkgFile.exists()) { 49 | throw new Exception(getArtifactName() + " generation failed!"); 50 | } 51 | 52 | return pkgFile; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateRpm.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | import org.redline_rpm.Builder; 11 | import org.redline_rpm.header.Architecture; 12 | import org.redline_rpm.header.Os; 13 | import org.redline_rpm.header.RpmType; 14 | 15 | import io.github.fvarrui.javapackager.utils.FileUtils; 16 | import io.github.fvarrui.javapackager.utils.Logger; 17 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 18 | 19 | /** 20 | * Creates a RPM package file including all app folder's content only for 21 | * GNU/Linux so app could be easily distributed on Gradle context 22 | */ 23 | public class GenerateRpm extends ArtifactGenerator { 24 | 25 | public GenerateRpm() { 26 | super("RPM package"); 27 | } 28 | 29 | @Override 30 | public boolean skip(LinuxPackager packager) { 31 | return !packager.getLinuxConfig().isGenerateRpm(); 32 | } 33 | 34 | @Override 35 | protected File doApply(LinuxPackager packager) throws Exception { 36 | 37 | File appFolder = packager.getAppFolder(); 38 | String name = packager.getName(); 39 | String version = packager.getVersion().replaceAll("-", "_"); 40 | String description = packager.getDescription(); 41 | String organizationName = packager.getOrganizationName(); 42 | File outputDirectory = packager.getOutputDirectory(); 43 | File executable = packager.getExecutable(); 44 | File assetsFolder = packager.getAssetsFolder(); 45 | String jreDirectoryName = packager.getJreDirectoryName(); 46 | Architecture arch = packager.getArch().toRpmArchitecture(); 47 | File mimeXmlFile = packager.getMimeXmlFile(); 48 | String installationPath = packager.getLinuxConfig().getInstallationPath(); 49 | String appPath = installationPath + "/" + name; 50 | 51 | // generates desktop file from velocity template 52 | File desktopFile = new File(assetsFolder, name + ".desktop"); 53 | VelocityUtils.render("linux/desktop.vtl", desktopFile, packager); 54 | Logger.info("Desktop file rendered in " + desktopFile.getAbsolutePath()); 55 | 56 | // copies desktop file to app 57 | FileUtils.copyFileToFolder(desktopFile, appFolder); 58 | 59 | // creates RPM builder 60 | Builder builder = new Builder(); 61 | builder.setType(RpmType.BINARY); 62 | builder.setPlatform(arch, Os.LINUX); 63 | builder.setPackage(name, version, "1"); 64 | builder.setPackager(organizationName); 65 | builder.setDescription(description); 66 | builder.setPrefixes(installationPath); 67 | 68 | // list of files which needs execution permissions 69 | List executionPermissions = new ArrayList<>(); 70 | executionPermissions.add(executable); 71 | executionPermissions.add(new File(appFolder, jreDirectoryName + "/bin/java")); 72 | executionPermissions.add(new File(appFolder, jreDirectoryName + "/lib/jspawnhelper")); 73 | 74 | // add all app files 75 | addDirectory(builder, installationPath, appFolder, executionPermissions); 76 | 77 | // link to desktop file 78 | addLink(builder, "/usr/share/applications/" + desktopFile.getName(), appPath + "/" + desktopFile.getName()); 79 | 80 | // copy and link to mime.xml file 81 | if (mimeXmlFile != null) { 82 | FileUtils.copyFileToFolder(mimeXmlFile, appFolder); 83 | addLink(builder, "/usr/share/mime/packages/" + mimeXmlFile.getName(), appPath + "/" + mimeXmlFile.getName()); 84 | } 85 | 86 | // link to binary 87 | addLink(builder, "/usr/local/bin/" + executable.getName(), appPath + "/" + executable.getName()); 88 | 89 | // add all app files 90 | addDirectory(builder, installationPath, appFolder, executionPermissions); 91 | 92 | // build RPM file 93 | builder.build(outputDirectory); 94 | 95 | // renames generated RPM file if created 96 | String suffix = "-1." + arch + ".rpm"; 97 | File originalRpm = new File(outputDirectory, name + "-" + version + suffix); 98 | File rpm = null; 99 | if (originalRpm.exists()) { 100 | rpm = new File(outputDirectory, name + "_" + version + ".rpm"); 101 | if (rpm.exists()) rpm.delete(); 102 | FileUtils.rename(originalRpm, rpm.getName()); 103 | } 104 | 105 | return rpm; 106 | } 107 | 108 | private void addLink(Builder builder, String path, String target) throws NoSuchAlgorithmException, IOException { 109 | Logger.info("Adding link '" + path + "' to RPM builder targeting '" + target + "'"); 110 | builder.addLink(path, target); 111 | } 112 | 113 | private void addFile(Builder builder, String rootPath, File file, int mode) throws NoSuchAlgorithmException, IOException { 114 | String filePath = rootPath + "/" + file.getName(); 115 | Logger.info("Adding file '" + file + "' to RPM builder as '" + filePath + "'"); 116 | builder.addFile(filePath, file, mode); 117 | } 118 | 119 | private void addDirectory(Builder builder, String parentPath, File directory, List executionPermissions) throws NoSuchAlgorithmException, IOException { 120 | String dirPath = parentPath + "/" + directory.getName(); 121 | Logger.info("Adding directory '" + directory + "' to RPM builder as '" + dirPath + "'"); 122 | builder.addDirectory(dirPath); 123 | for (File f : Objects.requireNonNull(directory.listFiles())) { 124 | if (f.isDirectory()) 125 | addDirectory(builder, dirPath, f, executionPermissions); 126 | else { 127 | addFile(builder, dirPath, f, executionPermissions.contains(f) ? 0755 : 0644); 128 | } 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/GenerateSetup.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | import io.github.fvarrui.javapackager.utils.CommandUtils; 7 | import io.github.fvarrui.javapackager.utils.FileUtils; 8 | import io.github.fvarrui.javapackager.utils.Logger; 9 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 10 | import net.jsign.WindowsSigner; 11 | 12 | /** 13 | * Creates a Setup file including all app folder's content only for 14 | * Windows so app could be easily distributed 15 | */ 16 | public class GenerateSetup extends ArtifactGenerator { 17 | 18 | public GenerateSetup() { 19 | super("Setup installer"); 20 | } 21 | 22 | @Override 23 | public boolean skip(WindowsPackager packager) { 24 | 25 | if (!packager.getWinConfig().isGenerateSetup()) { 26 | return true; 27 | } 28 | 29 | if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) { 30 | Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!"); 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | @Override 38 | protected File doApply(WindowsPackager packager) throws Exception { 39 | 40 | File iconFile = packager.getIconFile(); 41 | File assetsFolder = packager.getAssetsFolder(); 42 | String name = packager.getName(); 43 | File outputDirectory = packager.getOutputDirectory(); 44 | String version = packager.getVersion(); 45 | 46 | // copies ico file to assets folder 47 | FileUtils.copyFileToFolder(iconFile, assetsFolder); 48 | 49 | // generates iss file from velocity template 50 | File issFile = new File(assetsFolder, name + ".iss"); 51 | VelocityUtils.render("windows/iss.vtl", issFile, packager); 52 | 53 | // generates Windows installer with inno setup command line compiler 54 | CommandUtils.execute("iscc", "/O" + outputDirectory.getAbsolutePath(), "/F" + name + "_" + version, issFile); 55 | 56 | // setup file 57 | File setupFile = new File(outputDirectory, name + "_" + version + ".exe"); 58 | if (!setupFile.exists()) { 59 | throw new Exception("Windows setup file generation failed!"); 60 | } 61 | 62 | // sign installer 63 | WindowsSigner.sign(setupFile, packager.getDisplayName(), packager.getUrl(), packager.getWinConfig().getSigning()); 64 | 65 | return setupFile; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/LinuxPackager.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | import java.util.stream.Collectors; 6 | 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import io.github.fvarrui.javapackager.model.Platform; 10 | import io.github.fvarrui.javapackager.utils.FileUtils; 11 | import io.github.fvarrui.javapackager.utils.Logger; 12 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 13 | 14 | /** 15 | * Packager for GNU/Linux 16 | */ 17 | public class LinuxPackager extends Packager { 18 | 19 | private File desktopFile; 20 | private File mimeXmlFile = null; 21 | 22 | public LinuxPackager() { 23 | super(); 24 | installerGenerators.addAll(Context.getContext().getInstallerGenerators(Platform.linux)); 25 | } 26 | 27 | public File getDesktopFile() { 28 | return desktopFile; 29 | } 30 | 31 | public File getMimeXmlFile() { 32 | return mimeXmlFile; 33 | } 34 | 35 | @Override 36 | public void doInit() throws Exception { 37 | 38 | // sets linux config default values 39 | this.linuxConfig.setDefaults(this); 40 | 41 | } 42 | 43 | @Override 44 | protected void doCreateAppStructure() throws Exception { 45 | 46 | // sets common folders 47 | this.executableDestinationFolder = appFolder; 48 | this.jarFileDestinationFolder = appFolder; 49 | this.jreDestinationFolder = new File(appFolder, jreDirectoryName); 50 | this.resourcesDestinationFolder = appFolder; 51 | 52 | } 53 | 54 | /** 55 | * Creates a GNU/Linux app folder with native executable 56 | */ 57 | @Override 58 | public File doCreateApp() throws Exception { 59 | 60 | Logger.infoIndent("Creating GNU/Linux executable ..."); 61 | 62 | // sets executable file 63 | this.executable = new File(appFolder, name); 64 | 65 | // process classpath 66 | if (classpath != null) { 67 | classpaths = Arrays.asList(classpath.split("[:;]")); 68 | if (!isUseResourcesAsWorkingDir()) { 69 | classpaths = classpaths.stream().map(cp -> new File(cp).isAbsolute() ? cp : "$SCRIPTPATH/" + cp).collect(Collectors.toList()); 70 | } 71 | classpath = StringUtils.join(classpaths, ":"); 72 | } 73 | 74 | // generates desktop file from velocity template 75 | desktopFile = new File(assetsFolder, name + ".desktop"); 76 | VelocityUtils.render("linux/desktop.vtl", desktopFile, this); 77 | Logger.info("Rendering desktop file to " + desktopFile.getAbsolutePath()); 78 | 79 | // generates mime.xml file from velocity template 80 | if (isThereFileAssociations()) { 81 | mimeXmlFile = new File(assetsFolder, name + ".xml"); 82 | VelocityUtils.render("linux/mime.xml.vtl", mimeXmlFile, this); 83 | Logger.info("Rendering mime.xml file to " + mimeXmlFile.getAbsolutePath()); 84 | } 85 | 86 | // generates startup.sh script to boot java app 87 | File startupFile = new File(assetsFolder, "startup.sh"); 88 | VelocityUtils.render("linux/startup.sh.vtl", startupFile, this); 89 | Logger.info("Startup script generated in " + startupFile.getAbsolutePath()); 90 | 91 | // concats linux startup.sh script + generated jar in executable (binary) 92 | if (getLinuxConfig().isWrapJar()) 93 | FileUtils.concat(executable, startupFile, jarFile); 94 | else { 95 | FileUtils.copyFileToFile(startupFile, executable); 96 | FileUtils.copyFileToFolder(jarFile, appFolder); 97 | } 98 | 99 | // sets execution permissions 100 | executable.setExecutable(true, false); 101 | 102 | Logger.infoUnindent("GNU/Linux executable created in " + executable.getAbsolutePath() + "!"); 103 | 104 | return appFolder; 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/PackagerFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | 5 | import io.github.fvarrui.javapackager.model.Platform; 6 | 7 | /** 8 | * Packager factory 9 | */ 10 | public class PackagerFactory { 11 | 12 | public static Packager createPackager(Platform platform) throws Exception { 13 | if (platform == Platform.auto || platform == null) platform = Platform.getCurrentPlatform(); 14 | Packager packager = null; 15 | switch (platform) { 16 | case mac: 17 | packager = new MacPackager(); break; 18 | case linux: 19 | packager = new LinuxPackager(); break; 20 | case windows: 21 | packager = new WindowsPackager(); break; 22 | default: 23 | throw new Exception("Unsupported operating system: " + SystemUtils.OS_NAME + " " + SystemUtils.OS_VERSION + " " + SystemUtils.OS_ARCH); 24 | } 25 | packager.platform(platform); 26 | return packager; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/packagers/WindowsPackager.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.packagers; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | import java.util.stream.Collectors; 6 | 7 | import net.jsign.WindowsSigner; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import io.github.fvarrui.javapackager.model.Arch; 11 | import io.github.fvarrui.javapackager.model.Platform; 12 | import io.github.fvarrui.javapackager.utils.Logger; 13 | import io.github.fvarrui.javapackager.utils.VelocityUtils; 14 | 15 | /** 16 | * Packager for Windows 17 | */ 18 | public class WindowsPackager extends Packager { 19 | 20 | private File manifestFile; 21 | private File msmFile; 22 | 23 | public File getManifestFile() { 24 | return manifestFile; 25 | } 26 | 27 | public File getMsmFile() { 28 | return msmFile; 29 | } 30 | 31 | public void setMsmFile(File msmFile) { 32 | this.msmFile = msmFile; 33 | } 34 | 35 | public WindowsPackager() { 36 | super(); 37 | platform(Platform.windows); 38 | } 39 | 40 | @Override 41 | public void doInit() throws Exception { 42 | 43 | // sets default system architecture 44 | if (getArch() != Arch.x64 && getArch() != Arch.x86) { 45 | if (Platform.windows.isCurrentPlatform()) 46 | arch(Arch.getDefault()); 47 | else 48 | arch(Arch.x64); 49 | } 50 | 51 | // sets windows config default values 52 | this.winConfig.setDefaults(this); 53 | 54 | } 55 | 56 | @Override 57 | protected void doCreateAppStructure() throws Exception { 58 | 59 | // sets common folders 60 | this.executableDestinationFolder = appFolder; 61 | this.jarFileDestinationFolder = appFolder; 62 | this.jreDestinationFolder = new File(appFolder, jreDirectoryName); 63 | this.resourcesDestinationFolder = appFolder; 64 | 65 | } 66 | 67 | /** 68 | * Creates a Windows app file structure with native executable 69 | */ 70 | @Override 71 | public File doCreateApp() throws Exception { 72 | 73 | Logger.infoIndent("Creating windows EXE ... with " + getWinConfig().getExeCreationTool()); 74 | 75 | // generates manifest file to require administrator privileges from velocity template 76 | manifestFile = new File(assetsFolder, name + ".exe.manifest"); 77 | VelocityUtils.render("windows/exe.manifest.vtl", manifestFile, this); 78 | Logger.info("Exe manifest file generated in " + manifestFile.getAbsolutePath() + "!"); 79 | 80 | // sets executable file 81 | executable = new File(appFolder, name + ".exe"); 82 | 83 | // process classpath 84 | if (classpath != null) { 85 | classpaths = Arrays.asList(classpath.split("[;:]")); 86 | if (!isUseResourcesAsWorkingDir()) { 87 | classpaths = classpaths.stream().map(cp -> new File(cp).isAbsolute() ? cp : "%EXEDIR%/" + cp).collect(Collectors.toList()); 88 | } 89 | classpath = StringUtils.join(classpaths, ";"); 90 | } 91 | 92 | // invokes Windows exe artifact generator (building tool dependant) 93 | executable = Context.getContext().createWindowsExe(this); 94 | 95 | // signs the executable 96 | WindowsSigner.sign(executable, getDisplayName(), getUrl(), getWinConfig().getSigning()); 97 | 98 | Logger.infoUnindent("Windows EXE file created in " + executable + "!"); 99 | 100 | return appFolder; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/CharsetUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.nio.charset.Charset; 4 | import java.nio.charset.StandardCharsets; 5 | 6 | import org.apache.commons.lang3.SystemUtils; 7 | 8 | public class CharsetUtil { 9 | 10 | private static Charset commandLineCharset; 11 | 12 | public static Charset getCommandLineCharset(){ 13 | if (commandLineCharset == null) { 14 | if (SystemUtils.IS_OS_WINDOWS) { 15 | commandLineCharset = chcp(); 16 | } else { 17 | commandLineCharset = Charset.defaultCharset(); 18 | } 19 | } 20 | return commandLineCharset; 21 | } 22 | 23 | private static Charset chcp() { 24 | try{ 25 | String result = CommandUtils.run("cmd", "/k", "chcp"); 26 | String code = StringUtils.find("\\d+", result); 27 | Logger.debug("'chcp' code found: " + code); 28 | switch (code){ 29 | case "37": 30 | case "037": return Charset.forName("IBM037"); 31 | case "936": return Charset.forName("gb2312"); 32 | case "950": return Charset.forName("big5"); 33 | case "1145": return Charset.forName("IBM01145"); 34 | case "1200": return StandardCharsets.UTF_16; 35 | case "51936": return Charset.forName("EUC-CN"); 36 | case "65001": return StandardCharsets.UTF_8; 37 | } 38 | } catch (Exception e){ 39 | // do nothing 40 | } 41 | return Charset.defaultCharset(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/CommandUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.util.List; 8 | 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.codehaus.plexus.util.cli.CommandLineException; 11 | 12 | /** 13 | * Command utils 14 | */ 15 | public class CommandUtils { 16 | 17 | public static String executeOnDirectory(File workingDirectory, String executable, Object... arguments) throws IOException, CommandLineException { 18 | ExecutionResult result = executeWithResult(workingDirectory, executable, arguments); 19 | if (result.getExitCode() != 0) { 20 | throw new CommandLineException("Command execution failed: " + executable + " " + StringUtils.join(arguments, " ")); 21 | } 22 | return result.getOutput(); 23 | } 24 | 25 | public static String execute(File executable, Object... arguments) throws IOException, CommandLineException { 26 | return execute(executable.getAbsolutePath(), arguments); 27 | } 28 | 29 | public static String execute(String executable, Object... arguments) throws IOException, CommandLineException { 30 | return executeOnDirectory(new File("."), executable, arguments); 31 | } 32 | 33 | public static String execute(String executable, List arguments) throws IOException, CommandLineException { 34 | return executeOnDirectory(new File("."), executable, arguments.toArray(new Object[0])); 35 | } 36 | 37 | public static ExecutionResult executeWithResult(File workingDirectory, String executable, Object... arguments) throws IOException, CommandLineException { 38 | ExecutionResult result = new ExecutionResult(); 39 | 40 | StringBuffer outputBuffer = new StringBuffer(); 41 | StringBuffer errorBuffer = new StringBuffer(); 42 | 43 | Commandline command = new Commandline(); 44 | command.setWorkingDirectory(workingDirectory); 45 | command.setExecutable(executable); 46 | command.createArguments(arguments); 47 | command.toString(); 48 | 49 | String commandLine = command.getCommandLineAsString(); 50 | 51 | Logger.info("Executing command: " + commandLine); 52 | 53 | Process process = command.execute(); 54 | 55 | BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream(), CharsetUtil.getCommandLineCharset())); 56 | BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream())); 57 | while (process.isAlive() || output.ready() || error.ready()) { 58 | if (output.ready()) { 59 | String outputLine = output.readLine(); 60 | Logger.info(outputLine); 61 | outputBuffer.append(outputLine + "\n"); 62 | } 63 | if (error.ready()) { 64 | String errorLine = error.readLine(); 65 | Logger.error(errorLine); 66 | errorBuffer.append(errorLine + "\n"); 67 | } 68 | } 69 | output.close(); 70 | error.close(); 71 | 72 | result.setCommandLine(commandLine); 73 | result.setOutput(outputBuffer.toString()); 74 | result.setError(errorBuffer.toString()); 75 | result.setExitCode(process.exitValue()); 76 | 77 | return result; 78 | } 79 | 80 | public static String run(String ... command) throws IOException { 81 | Process p = Runtime.getRuntime().exec(command); 82 | BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); 83 | String result = br.readLine(); 84 | br.close(); 85 | return result; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/Commandline.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.File; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.apache.commons.lang3.SystemUtils; 7 | 8 | /** 9 | * Commandline 10 | */ 11 | public class Commandline extends org.codehaus.plexus.util.cli.Commandline { 12 | 13 | public Commandline() { 14 | super(); 15 | getShell().setQuotedArgumentsEnabled(false); 16 | if (SystemUtils.IS_OS_WINDOWS) { 17 | getShell().setShellArgs(new String[] { "/s", "/c" } ); 18 | } 19 | } 20 | 21 | public String[] getCommandline() 22 | { 23 | return getShellCommandline(); 24 | } 25 | 26 | public void createArguments(Object... arguments) { 27 | for (Object argument : arguments) { 28 | 29 | if (argument == null) 30 | continue; 31 | 32 | if (argument.getClass().isArray()) { 33 | createArguments((Object[])argument); 34 | continue; 35 | } 36 | 37 | if (argument instanceof File) { 38 | 39 | File argFile = (File) argument; 40 | if (argFile.getName().contains("*")) { 41 | argument = org.codehaus.plexus.util.StringUtils.quoteAndEscape(argFile.getParentFile().getAbsolutePath(), '\"') + File.separator + argFile.getName(); 42 | } else { 43 | argument = ((File) argument).getAbsolutePath(); 44 | } 45 | 46 | } 47 | 48 | String arg = argument.toString().trim(); 49 | if (!arg.contains("\"") && StringUtils.containsWhitespace(arg)) { 50 | arg = org.codehaus.plexus.util.StringUtils.quoteAndEscape(arg, '\"'); 51 | } 52 | this.createArg().setValue(arg); 53 | 54 | } 55 | } 56 | 57 | public String getCommandLineAsString() { 58 | return StringUtils.join(getCommandline(), " "); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/ExecutionResult.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | /** 4 | * Command execution result 5 | */ 6 | public class ExecutionResult { 7 | 8 | private String commandLine; 9 | private int exitCode; 10 | private String output; 11 | private String error; 12 | 13 | public String getCommandLine() { 14 | return commandLine; 15 | } 16 | 17 | public void setCommandLine(String commandLine) { 18 | this.commandLine = commandLine; 19 | } 20 | 21 | public int getExitCode() { 22 | return exitCode; 23 | } 24 | 25 | public void setExitCode(int exitCode) { 26 | this.exitCode = exitCode; 27 | } 28 | 29 | public String getOutput() { 30 | return output; 31 | } 32 | 33 | public void setOutput(String output) { 34 | this.output = output; 35 | } 36 | 37 | public String getError() { 38 | return error; 39 | } 40 | 41 | public void setError(String error) { 42 | this.error = error; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "ExecutionResult [commandLine=" + commandLine + ", exitCode=" + exitCode + ", output=" + output 48 | + ", error=" + error + "]"; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/IconUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import io.github.fvarrui.javapackager.model.Platform; 4 | 5 | /** 6 | * Icon utils 7 | */ 8 | public class IconUtils { 9 | 10 | public static String getIconFileExtensionByPlatform(Platform platform) { 11 | switch (platform) { 12 | case linux: return ".png"; 13 | case mac: return ".icns"; 14 | case windows: return ".ico"; 15 | default: return null; 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/JDKUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Properties; 10 | 11 | import io.github.fvarrui.javapackager.model.Arch; 12 | import io.github.fvarrui.javapackager.model.Platform; 13 | 14 | /** 15 | * JDK utils 16 | */ 17 | public class JDKUtils { 18 | 19 | /** 20 | * Converts "release" file from specified JDK or JRE to map 21 | * 22 | * @param jdkPath JDK directory path 23 | * @return Map with all properties 24 | * @throws IOException release file could not be read 25 | */ 26 | public static Map getRelease(File jdkPath) throws IOException { 27 | Map propertiesMap = new HashMap<>(); 28 | File releaseFile = new File(jdkPath, "release"); 29 | if (!releaseFile.exists()) { 30 | return null; 31 | } 32 | Properties properties = new Properties(); 33 | properties.load(new FileInputStream(releaseFile)); 34 | properties.forEach((key, value) -> propertiesMap.put(key.toString(), value.toString().replaceAll("^\"|\"$", ""))); 35 | return propertiesMap; 36 | } 37 | 38 | /** 39 | * Checks if the platform specified in the "release" map matches the required 40 | * platform 41 | * 42 | * @param platform Platform to match 43 | * @param releaseMap Map containing all JDK/JRE release file's properties 44 | * @return true if JDK is for platform 45 | */ 46 | private static boolean checkPlatform(Platform platform, Map releaseMap) { 47 | try { 48 | return (releaseMap.get("OS_NAME") == null || platform == Platform.getPlatform(releaseMap.get("OS_NAME"))); 49 | } catch (IllegalArgumentException e) { 50 | return false; 51 | } 52 | } 53 | 54 | /** 55 | * Checks if the architecture specified in the "release" map matches the required 56 | * architecture 57 | * 58 | * @param arch Platform to match 59 | * @param releaseMap Map containing all JDK/JRE release file's properties 60 | * @return true if JDK is for platform 61 | */ 62 | private static boolean checkArchitecture(Arch arch, Map releaseMap) { 63 | try { 64 | return (releaseMap.get("OS_ARCH") == null || arch == null || arch == Arch.getArch(releaseMap.get("OS_ARCH"))); 65 | } catch (IllegalArgumentException e) { 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * Checks if the image type specified in the "release" map matches the required 72 | * type 73 | * 74 | * @param type Image type to match 75 | * @param releaseMap Map containing all JDK/JRE release file's properties 76 | * @return true if JDK is for platform 77 | */ 78 | private static boolean checkImageType(String imageType, Map releaseMap) { 79 | try { 80 | return (releaseMap.get("IMAGE_TYPE") == null || imageType.equals(releaseMap.get("IMAGE_TYPE"))); 81 | } catch (IllegalArgumentException e) { 82 | return false; 83 | } 84 | } 85 | 86 | /** 87 | * Checks if a JDK is for platform and architecture 88 | * 89 | * @param platform Specific platform 90 | * @param arch Specific architecture 91 | * @param jdkPath Path to the JDK folder 92 | * @return true if is valid, otherwise false 93 | * @throws FileNotFoundException Path to JDK not found 94 | * @throws IOException Error reading JDK "release" file 95 | */ 96 | public static boolean isValidJDK(Platform platform, Arch arch, File jdkPath) throws FileNotFoundException, IOException { 97 | if (jdkPath == null || !jdkPath.isDirectory()) { 98 | return false; 99 | } 100 | Map releaseMap = getRelease(jdkPath); 101 | if (releaseMap != null) { 102 | return checkPlatform(platform, releaseMap) && checkArchitecture(arch, releaseMap) && checkImageType("JDK", releaseMap); 103 | } 104 | return true; 105 | } 106 | 107 | /** 108 | * Checks if a JDK is for platform 109 | * 110 | * @param platform Specific platform 111 | * @param jdkPath Path to the JDK folder 112 | * @return true if is valid, otherwise false 113 | * @throws FileNotFoundException Path to JDK not found 114 | * @throws IOException Error reading JDK "release" file 115 | */ 116 | public static boolean isValidJDK(Platform platform, File jdkPath) throws FileNotFoundException, IOException { 117 | return isValidJDK(platform, null, jdkPath); 118 | } 119 | 120 | /** 121 | * Checks if a JRE is for platform and architecture 122 | * 123 | * @param platform Specific platform 124 | * @param arch Specific architecture 125 | * @param jrePath Path to the JRE folder 126 | * @return true if is valid, otherwise false 127 | * @throws IOException Error reading JDK "release" file 128 | */ 129 | public static boolean isValidJRE(Platform platform, Arch arch, File jrePath) throws IOException { 130 | if (jrePath == null || !jrePath.isDirectory()) { 131 | return false; 132 | } 133 | Map releaseMap = getRelease(jrePath); 134 | if (releaseMap != null) { 135 | return checkPlatform(platform, releaseMap) && checkArchitecture(arch, releaseMap); 136 | } else if (new File(jrePath, "bin/java").exists() || new File(jrePath, "bin/java.exe").exists()) { 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | /** 143 | * Checks if a JRE is for platform 144 | * 145 | * @param platform Specific platform 146 | * @param jrePath Path to the JRE folder 147 | * @return true if is valid, otherwise false 148 | * @throws IOException Error reading JDK "release" file 149 | */ 150 | public static boolean isValidJRE(Platform platform, File jrePath) throws IOException { 151 | return isValidJRE(platform, null, jrePath); 152 | } 153 | 154 | /** 155 | * Checks if release's file property "IMAGE_TYPE==JDK" 156 | * @param jdkPath JDK path 157 | * @return true if is a JRE 158 | * @throws IOException Error reading JDK "release" file 159 | */ 160 | public static boolean isJDK(File jdkPath) throws IOException { 161 | Map releaseMap = getRelease(jdkPath); 162 | if (releaseMap != null) { 163 | return releaseMap.get("IMAGE_TYPE") == "JDK"; 164 | } 165 | return true; 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/JarUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.codehaus.plexus.util.cli.CommandLineException; 7 | 8 | import io.github.fvarrui.javapackager.packagers.Context; 9 | 10 | public class JarUtils { 11 | 12 | /** 13 | * Runs "jar uf jarfile newfile" to add newfile into jarfile. 14 | * @param jarFile JAR file 15 | * @param newFile File to add to jar file 16 | * @throws IOException If something related to IO went wrong 17 | * @throws CommandLineException If something related to command execution went wrong 18 | */ 19 | public static void addFileToJar(File jarFile, File newFile) throws IOException, CommandLineException { 20 | File jar = new File(Context.getContext().getDefaultToolchain(), "/bin/jar"); 21 | CommandUtils.executeOnDirectory(newFile.getParentFile(), jar.getAbsolutePath(), "uf", jarFile, newFile.getName()); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/Logger.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import io.github.fvarrui.javapackager.packagers.Context; 6 | 7 | /** 8 | * Logging class 9 | */ 10 | public class Logger { 11 | 12 | private static final String TAB = " "; // uses four blank spaces as tab 13 | 14 | private static int tabs = 0; 15 | 16 | public static String error(String error) { 17 | if (Context.isMaven()) Context.getMavenContext().getLogger().error(StringUtils.repeat(TAB, tabs) + error); 18 | if (Context.isGradle()) Context.getGradleContext().getLogger().error(StringUtils.repeat(TAB, tabs) + error); 19 | return error; 20 | } 21 | 22 | public static String error(String error, Throwable t) { 23 | if (Context.isMaven()) { 24 | Context.getMavenContext().getLogger().error(StringUtils.repeat(TAB, tabs) + error); 25 | Context.getMavenContext().getLogger().error(t); 26 | } 27 | if (Context.isGradle()) { 28 | Context.getGradleContext().getLogger().error(StringUtils.repeat(TAB, tabs) + error, t); 29 | } 30 | return error; 31 | } 32 | 33 | public static String warn(String warn) { 34 | if (Context.isMaven()) Context.getMavenContext().getLogger().warn(StringUtils.repeat(TAB, tabs) + warn); 35 | if (Context.isGradle()) Context.getGradleContext().getLogger().warn(StringUtils.repeat(TAB, tabs) + warn); 36 | return warn; 37 | } 38 | 39 | public static String info(String info) { 40 | if (Context.isMaven()) Context.getMavenContext().getLogger().info(StringUtils.repeat(TAB, tabs) + info); 41 | if (Context.isGradle()) Context.getGradleContext().getLogger().quiet(StringUtils.repeat(TAB, tabs) + info); 42 | return info; 43 | } 44 | 45 | public static String debug(String debug) { 46 | if (Context.isMaven()) Context.getMavenContext().getLogger().debug(StringUtils.repeat(TAB, tabs) + debug); 47 | if (Context.isGradle()) Context.getGradleContext().getLogger().debug(StringUtils.repeat(TAB, tabs) + debug); 48 | return debug; 49 | } 50 | 51 | public static void infoIndent(String msg) { 52 | info(msg); 53 | tabs++; 54 | } 55 | 56 | public static void infoUnindent(String msg) { 57 | tabs--; 58 | info(msg); 59 | info(""); 60 | } 61 | 62 | public static void warnUnindent(String msg) { 63 | tabs--; 64 | warn(msg); 65 | info(""); 66 | } 67 | 68 | public static void errorUnindent(String msg) { 69 | tabs--; 70 | error(msg); 71 | info(""); 72 | } 73 | 74 | public static void errorUnindent(String msg, Throwable t) { 75 | tabs--; 76 | error(msg, t); 77 | info(""); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/MojoExecutorUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.twdata.maven.mojoexecutor.MojoExecutor.Element; 8 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 9 | 10 | /** 11 | * Mojo executor utils 12 | */ 13 | public class MojoExecutorUtils { 14 | 15 | public static List mapToElementsList(Map map) { 16 | List elements = new ArrayList<>(); 17 | map.entrySet().forEach(entry -> elements.add(element(entry.getKey(), entry.getValue()))); 18 | return elements; 19 | } 20 | 21 | public static Element [] mapToElementsArray(Map map) { 22 | List elements = mapToElementsList(map); 23 | return elements.toArray(new Element[elements.size()]); 24 | } 25 | 26 | public static Element mapToElement(String name, Map map) { 27 | return element(name, mapToElementsArray(map)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/ObjectUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.util.Arrays; 4 | import java.util.Optional; 5 | 6 | import org.codehaus.plexus.util.StringUtils; 7 | 8 | /** 9 | * Object utils 10 | */ 11 | public class ObjectUtils { 12 | 13 | /** 14 | * Returns the first non-null object 15 | * @param Type 16 | * @param values List of objects 17 | * @return First non-null object from values list 18 | */ 19 | @SuppressWarnings("unchecked") 20 | public static T defaultIfNull(final T ... values) { 21 | Optional value = Arrays.asList(values).stream().filter(v -> v != null).findFirst(); 22 | if (value.isPresent()) return value.get(); 23 | return null; 24 | } 25 | 26 | /** 27 | * Returns the first non-blank String 28 | * @param values List of String 29 | * @return First non-blank string 30 | */ 31 | public static String defaultIfBlank(final String ... values) { 32 | Optional value = Arrays.asList(values).stream().filter(v -> v != null && !StringUtils.isBlank(v)).findFirst(); 33 | if (value.isPresent()) return value.get(); 34 | return null; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/RcEdit.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.File; 4 | 5 | import static io.github.fvarrui.javapackager.utils.CommandUtils.execute; 6 | 7 | public class RcEdit { 8 | 9 | private File rcedit; 10 | 11 | public RcEdit(File outputDir) throws Exception { 12 | rcedit = new File(outputDir, "rcedit.exe"); 13 | if (!rcedit.exists()) { 14 | FileUtils.copyResourceToFile("/windows/rcedit-x64.exe", rcedit); 15 | } 16 | } 17 | 18 | public RcEdit() throws Exception { 19 | this(new File(System.getProperty("java.io.tmpdir"))); 20 | } 21 | 22 | private void setExeMetadata(File executable, String option, String key, Object value) throws Exception { 23 | execute(rcedit, executable, option, key, value); 24 | } 25 | 26 | private void setExeMetadata(File executable, String option, Object value) throws Exception { 27 | execute(rcedit, executable, option, value); 28 | } 29 | 30 | public void setIcon(File executable, File icon) throws Exception { 31 | setExeMetadata(executable, "--set-icon", icon); 32 | } 33 | 34 | public void setManifest(File executable, File manifest) throws Exception { 35 | setExeMetadata(executable, "--application-manifest", manifest); 36 | } 37 | 38 | public void setFileVersion(File executable, String fileVersion) throws Exception { 39 | setExeMetadata(executable, "--set-file-version", fileVersion); 40 | } 41 | 42 | public void setProductVersion(File executable, String productVersion) throws Exception { 43 | setExeMetadata(executable, "--set-product-version", productVersion); 44 | } 45 | 46 | public void setVersionString(File executable, String key, String value) throws Exception { 47 | setExeMetadata(executable, "--set-version-string", key, value); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class StringUtils { 7 | 8 | public static String dosToUnix(String input) { 9 | return input.replaceAll("\\r\\n", "\n").replaceAll("\\r", "\n"); 10 | } 11 | 12 | public static String find(String pattern, String data) { 13 | Pattern r = Pattern.compile(pattern); 14 | Matcher matcher = r.matcher(data); 15 | matcher.find(); 16 | return matcher.group(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/ThreadUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | /** 4 | * Thread utils 5 | */ 6 | public class ThreadUtils { 7 | 8 | public static void sleep(long millis) { 9 | try { 10 | Thread.sleep(millis); 11 | } catch (InterruptedException e) { 12 | Logger.error(e.getMessage()); 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/VelocityUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import static org.apache.commons.io.FileUtils.writeStringToFile; 4 | 5 | import java.io.File; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | 11 | import org.apache.velocity.Template; 12 | import org.apache.velocity.VelocityContext; 13 | import org.apache.velocity.app.VelocityEngine; 14 | import org.apache.velocity.runtime.RuntimeConstants; 15 | import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; 16 | import org.apache.velocity.runtime.resource.loader.FileResourceLoader; 17 | import org.apache.velocity.util.StringBuilderWriter; 18 | 19 | import io.github.fvarrui.javapackager.packagers.Packager; 20 | 21 | /** 22 | * Velocity utils 23 | */ 24 | public class VelocityUtils { 25 | 26 | private static File assetsDir = new File("assets"); 27 | private static VelocityEngine velocityEngine = null; 28 | private static List templates; 29 | 30 | private VelocityUtils() {} 31 | 32 | public static void init(Packager packager) { 33 | assetsDir = packager.getAssetsDir(); 34 | templates = packager.getTemplates() != null ? packager.getTemplates() : new ArrayList<>(); 35 | // add default template configs 36 | if (templates.stream().noneMatch(t -> t.getName().equals("windows/iss.vtl"))) { 37 | templates.add(new io.github.fvarrui.javapackager.model.Template("windows/iss.vtl", true)); 38 | } 39 | } 40 | 41 | private static VelocityEngine getVelocityEngine() { 42 | 43 | if (velocityEngine == null) { 44 | 45 | velocityEngine = new VelocityEngine(); 46 | 47 | // specify resource loaders to use 48 | velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file,class"); 49 | 50 | // for the loader 'file', set the FileResourceLoader as the class to use and use 'assets' directory for templates 51 | velocityEngine.setProperty("resource.loader.file.class", FileResourceLoader.class.getName()); 52 | velocityEngine.setProperty("resource.loader.file.path", assetsDir.getAbsolutePath()); 53 | 54 | // for the loader 'class', set the ClasspathResourceLoader as the class to use 55 | velocityEngine.setProperty("resource.loader.class.class", ClasspathResourceLoader.class.getName()); 56 | 57 | velocityEngine.init(); 58 | 59 | } 60 | 61 | return velocityEngine; 62 | } 63 | 64 | private static String render(String templatePath, Object info) throws Exception { 65 | VelocityContext context = new VelocityContext(); 66 | context.put("features", new ArrayList()); 67 | context.put("GUID", UUID.class); 68 | context.put("StringUtils", org.apache.commons.lang3.StringUtils.class); 69 | context.put("info", info); 70 | Template template = getVelocityEngine().getTemplate(templatePath, "UTF-8"); 71 | StringBuilderWriter writer = new StringBuilderWriter(); 72 | template.merge(context, writer); 73 | return writer.toString(); 74 | } 75 | 76 | private static void render(String templatePath, File output, Object info, boolean includeBom) throws Exception { 77 | String data = render(templatePath, info); 78 | data = StringUtils.dosToUnix(data); 79 | if (!includeBom) { 80 | writeStringToFile(output, data, "UTF-8"); 81 | } else { 82 | FileUtils.writeStringToFileWithBOM(output, data); 83 | } 84 | } 85 | 86 | public static void render(String templatePath, File output, Object info) throws Exception { 87 | Optional template = templates.stream().filter(t -> t.getName().equals(templatePath)).findFirst(); 88 | render(templatePath, output, info, template.isPresent() ? template.get().isBom() : false); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/VersionUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Java utils 8 | */ 9 | public class VersionUtils { 10 | 11 | /** 12 | * Splits a version string like x.y.z in an array of integers. If a part 13 | * of the version is not a number, the parsing process breaks and returns 14 | * till this numnber. 15 | * @param version Version string like "x.y.z" 16 | * @return An integer array like [ x, y, z ] 17 | */ 18 | private static Integer [] parseVersion(String version) { 19 | String [] splittedVersion = version.split("\\."); 20 | List parsedVersion = new ArrayList<>(); 21 | for (int i = 0; i < splittedVersion.length; i++) { 22 | try { 23 | parsedVersion.add(Integer.parseInt(splittedVersion[i])); 24 | } catch (NumberFormatException e) { 25 | break; 26 | } 27 | } 28 | return parsedVersion.toArray(new Integer[0]); 29 | } 30 | 31 | /** 32 | * Compares two strings with version numbers 33 | * @param v1 First version string 34 | * @param v2 Second version string 35 | * @return 0 if equals, -1 if v1 less than v2, +1 if v1 greater than v2 36 | */ 37 | public static int compareVersions(String v1, String v2) { 38 | Integer [] parsed1 = parseVersion(v1); 39 | Integer [] parsed2 = parseVersion(v2); 40 | int size = Math.min(parsed1.length, parsed2.length); 41 | for (int i = 0; i < size; i++) { 42 | if (parsed1[i] > parsed2[i]) return -1; 43 | if (parsed1[i] < parsed2[i]) return 1; 44 | } 45 | return 0; 46 | } 47 | 48 | /** 49 | * Returns Java runtime major version (e.g.: 7 for Java 1.7, 8 for Java 1.8, 50 | * 9 for Java 9, ..., and so on) 51 | * @return Java runtime major version 52 | */ 53 | public static int getJavaMajorVersion() { 54 | Integer [] parsed = parseVersion(System.getProperty("java.version")); 55 | int major = parsed[0]; 56 | if (major >= 2) return major; 57 | return parsed[1]; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/fvarrui/javapackager/utils/XMLUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.fvarrui.javapackager.utils; 2 | 3 | import java.io.File; 4 | 5 | import javax.xml.parsers.DocumentBuilder; 6 | import javax.xml.parsers.DocumentBuilderFactory; 7 | import javax.xml.transform.OutputKeys; 8 | import javax.xml.transform.Transformer; 9 | import javax.xml.transform.TransformerFactory; 10 | import javax.xml.transform.dom.DOMSource; 11 | import javax.xml.transform.stream.StreamResult; 12 | 13 | import org.w3c.dom.Document; 14 | import org.w3c.dom.DocumentType; 15 | import org.w3c.dom.Node; 16 | import org.w3c.dom.NodeList; 17 | 18 | /** 19 | * XML utils 20 | */ 21 | public class XMLUtils { 22 | 23 | /** 24 | * Pretiffy an XML file 25 | * @param file Xml file 26 | * @throws Exception Something went wrong 27 | */ 28 | public static final void prettify(File file) throws Exception { 29 | 30 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 31 | DocumentBuilder builder = factory.newDocumentBuilder(); 32 | Document document = builder.parse(file); 33 | 34 | trimWhitespace(document); 35 | 36 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); 37 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 38 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); 39 | transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 40 | 41 | DocumentType doctype = document.getDoctype(); 42 | if(doctype != null) { 43 | transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctype.getPublicId()); 44 | transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctype.getSystemId()); 45 | } 46 | 47 | transformer.transform(new DOMSource(document), new StreamResult(file)); 48 | 49 | } 50 | 51 | /** 52 | * Removes whitespaces from nodes 53 | * @param node Root node 54 | */ 55 | public static void trimWhitespace(Node node) { 56 | NodeList children = node.getChildNodes(); 57 | for(int i = 0; i < children.getLength(); ++i) { 58 | Node child = children.item(i); 59 | if(child.getNodeType() == Node.TEXT_NODE) { 60 | child.setTextContent(child.getTextContent().trim()); 61 | } 62 | trimWhitespace(child); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/jsign/WindowsSigner.java: -------------------------------------------------------------------------------- 1 | package net.jsign; 2 | 3 | import io.github.fvarrui.javapackager.model.WindowsSigning; 4 | import io.github.fvarrui.javapackager.utils.Logger; 5 | 6 | import java.io.File; 7 | 8 | public class WindowsSigner { 9 | 10 | private static final String TIMESTAMPING_AUTHORITY = "http://timestamp.comodoca.com/authenticode"; 11 | private static final Console CONSOLE = new Console() { 12 | @Override 13 | public void debug(String message) { 14 | Logger.debug(message); 15 | } 16 | 17 | public void info(String message) { 18 | Logger.info(message); 19 | } 20 | 21 | @Override 22 | public void warn(String message) { 23 | Logger.warn(message); 24 | } 25 | 26 | @Override 27 | public void warn(String message, Throwable throwable) { 28 | Logger.warn(message + " (" + throwable.getMessage() + ")"); 29 | } 30 | 31 | public void error(String message) { 32 | Logger.error(message); 33 | } 34 | }; 35 | 36 | // SINGLETON 37 | 38 | public static void sign(File file, String displayName, String url, WindowsSigning signing) { 39 | if (signing == null) { 40 | Logger.warn("No signing configuration found"); 41 | return; 42 | } 43 | Logger.infoIndent("Signing " + file); 44 | try { 45 | SignerHelper helper = new SignerHelper(CONSOLE, ""); 46 | helper.name(displayName); 47 | helper.url(url); 48 | helper.alg(signing.getAlg()); 49 | helper.keystore("" + signing.getKeystore()); 50 | helper.storepass(signing.getStorepass()); 51 | helper.storetype(signing.getStoretype()); 52 | helper.alias(signing.getAlias()); 53 | helper.certfile(signing.getCertfile()); 54 | helper.keyfile(signing.getKeyfile()); 55 | helper.keypass(signing.getKeypass()); 56 | helper.tsaurl(TIMESTAMPING_AUTHORITY); 57 | helper.sign(file); 58 | Logger.infoUnindent("Signed " + file); 59 | } catch (net.jsign.SignerException e) { 60 | Logger.errorUnindent(file + " could not be signed", e); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/linux/assembly.xml.vtl: -------------------------------------------------------------------------------- 1 | 2 | 6 | bundle 7 | false 8 | 9 | 10 | ${info.name} 11 | ${info.outputDirectory}/${info.name} 12 | 13 | ${info.executable.name} 14 | #if ($info.bundleJre) 15 | ${info.jreDirectoryName}/bin/* 16 | ${info.jreDirectoryName}/lib/jspawnhelper 17 | scripts/* 18 | #end 19 | 20 | 21 | 22 | ${info.name} 23 | ${info.outputDirectory}/${info.name} 24 | 25 | ${info.executable.name} 26 | #if ($info.bundleJre) 27 | ${info.jreDirectoryName}/bin/* 28 | ${info.jreDirectoryName}/lib/jspawnhelper 29 | scripts/* 30 | #end 31 | 32 | 0755 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/resources/linux/control.vtl: -------------------------------------------------------------------------------- 1 | Package: ${info.name} 2 | Version: ${info.version} 3 | Section: misc 4 | Priority: optional 5 | Architecture: ${info.arch.toDebArchitecture()} 6 | Maintainer: ${info.organizationName} <$!{info.organizationEmail}> 7 | Description: ${info.description} 8 | Distribution: stable 9 | #if(${info.url}) 10 | Homepage: ${info.url} 11 | #end -------------------------------------------------------------------------------- /src/main/resources/linux/default-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/linux/default-icon.png -------------------------------------------------------------------------------- /src/main/resources/linux/desktop-appimage.vtl: -------------------------------------------------------------------------------- 1 | #set ($categories = $StringUtils.join($info.linuxConfig.categories, ";")) 2 | [Desktop Entry] 3 | Name=${info.displayName} 4 | GenericName=${info.displayName} 5 | Comment=${info.description} 6 | Exec=${info.name} %U 7 | Icon=${info.name} 8 | Terminal=false 9 | Type=Application 10 | StartupNotify=true 11 | #if ($info.isThereFileAssociations()) 12 | MimeType=${info.getMimeTypesListAsString(";")} 13 | #end 14 | Categories=${categories} -------------------------------------------------------------------------------- /src/main/resources/linux/desktop.vtl: -------------------------------------------------------------------------------- 1 | #set ($categories = $StringUtils.join($info.linuxConfig.categories, ";")) 2 | [Desktop Entry] 3 | Name=${info.displayName} 4 | GenericName=${info.displayName} 5 | Comment=${info.description} 6 | Exec=${info.linuxConfig.installationPath}/${info.name}/${info.executable.name} %U 7 | Icon=${info.linuxConfig.installationPath}/${info.name}/${info.name}.png 8 | Terminal=false 9 | Type=Application 10 | StartupNotify=true 11 | #if ($info.isThereFileAssociations()) 12 | MimeType=${info.getMimeTypesListAsString(";")} 13 | #end 14 | Categories=${categories} 15 | -------------------------------------------------------------------------------- /src/main/resources/linux/mime.xml.vtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | #foreach ($fileAssociation in $info.fileAssociations) 4 | 5 | ${fileAssociation.description} 6 | 7 | 8 | #end 9 | -------------------------------------------------------------------------------- /src/main/resources/linux/startup.sh.vtl: -------------------------------------------------------------------------------- 1 | #set ($vmArgs = $StringUtils.join($info.vmArgs, " ")) 2 | #!/usr/bin/env bash 3 | # GNU/Linux startup script generated by JavaPackager plugin 4 | 5 | SCRIPTPATH=$(dirname "$(readlink -e "$0")") 6 | 7 | #if ($info.bundleJre) 8 | JAVA="$SCRIPTPATH/${info.jreDirectoryName}/bin/java" 9 | #else 10 | function showMessage() { 11 | if type -p notify-send; then 12 | notify-send -u critical "${info.name}" "$1" 13 | else 14 | echo $1 15 | fi 16 | } 17 | if type -p java > /dev/null; then 18 | JAVA=java 19 | elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 20 | JAVA="$JAVA_HOME/bin/java" 21 | else 22 | showMessage "Java not installed" 23 | exit 1 24 | fi 25 | #if ($info.jreMinVersion) 26 | function compareVersions() { 27 | [[ "$1" == "$2" ]] && return 0 28 | local IFS=. 29 | local i v1 v2 30 | read -r -a v1 <<< "$1" 31 | read -r -a v2 <<< "$2" 32 | for ((i=${#v1[@]}; i<${#v2[@]}; i++)) 33 | do 34 | v1[i]=0 35 | done 36 | for ((i=${#v2[@]}; i<${#v1[@]}; i++)) 37 | do 38 | v2[i]=0 39 | done 40 | for ((i=0; i<${#v1[@]}; i++)) 41 | do 42 | [[ "${v1[$i]}" > "${v2[$i]}" ]] && return 0 43 | [[ "${v1[$i]}" < "${v2[$i]}" ]] && return 1 44 | done 45 | return 0 46 | } 47 | JAVA_VERSION=$("$JAVA" -version 2>&1 | awk -F '"' '/version/ {print $2}') 48 | compareVersions "$JAVA_VERSION" "${info.jreMinVersion}" 49 | if [[ $? != 0 ]]; then 50 | showMessage "The application requires a Java Runtime Environment ${info.jreMinVersion} or higher." 51 | exit 1 52 | fi 53 | #end 54 | #end 55 | 56 | #if ($info.linuxConfig.wrapJar) 57 | BINARY="$SCRIPTPATH/${info.executable.name}" 58 | #else 59 | BINARY="$SCRIPTPATH/${info.jarFile.name}" 60 | #end 61 | 62 | #if ($info.envPath) 63 | export PATH=${info.envPath} 64 | #end 65 | 66 | JVMDefaultOptions="${vmArgs}" 67 | JVMOptionsFile="$SCRIPTPATH/${info.name}.l4j.ini" 68 | [ -f "${JVMOptionsFile}" ] && while read -r option; do JVMDefaultOptions+=" $option"; done <<< $(sed "/^#.*$/d" "${JVMOptionsFile}") 69 | 70 | JVMClassPath="$BINARY" 71 | #foreach ($classpath in $info.classpaths) 72 | JVMClassPath+=":${classpath}" 73 | #end 74 | 75 | #if ($info.useResourcesAsWorkingDir) 76 | cd "$SCRIPTPATH" 77 | #end 78 | 79 | #if ($info.scripts.bootstrap) 80 | # invoke bootstrap script 81 | Bootstrap="$SCRIPTPATH/scripts/${info.bootstrapFile.name}" && [ -x "$Bootstrap" ] && "$Bootstrap" 82 | #end 83 | 84 | #if ($info.administratorRequired) 85 | pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY GDKBACKEND=x11 "${JAVA}" ${JVMDefaultOptions} -jar "${JVMClassPath}" $@ 86 | #else 87 | "${JAVA}" ${JVMDefaultOptions} -jar "${JVMClassPath}" $@ 88 | #end 89 | exit 0 90 | -------------------------------------------------------------------------------- /src/main/resources/mac/Info.plist.vtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | ${info.displayName} ${info.version} 7 | #if (!$info.fileAssociations.isEmpty()) 8 | CFBundleDocumentTypes 9 | 10 | #foreach ($fileAssociation in $info.fileAssociations) 11 | 12 | CFBundleTypeIconFile 13 | ${info.iconFile.name} 14 | CFBundleTypeName 15 | ${fileAssociation.description} 16 | CFBundleTypeRole 17 | Viewer 18 | LSHandlerRank 19 | Owner 20 | LSItemContentTypes 21 | 22 | ${info.macConfig.appId}.${fileAssociation.extension} 23 | 24 | 25 | #end 26 | 27 | #end 28 | #if (!$info.fileAssociations.isEmpty()) 29 | UTExportedTypeDeclarations 30 | 31 | #foreach ($fileAssociation in $info.fileAssociations) 32 | 33 | UTTypeConformsTo 34 | 35 | public.data 36 | 37 | UTTypeDescription 38 | ${fileAssociation.description} 39 | UTTypeIdentifier 40 | ${info.macConfig.appId}.${fileAssociation.extension} 41 | UTTypeTagSpecification 42 | 43 | public.filename-extension 44 | ${fileAssociation.extension} 45 | #if ($fileAssociation.mimeType) 46 | public.mime-type 47 | ${fileAssociation.mimeType} 48 | #end 49 | 50 | 51 | #end 52 | 53 | #end 54 | CFBundleDevelopmentRegion 55 | English 56 | CFBundleExecutable 57 | ${info.executable.name} 58 | CFBundleIconFile 59 | ${info.iconFile.name} 60 | CFBundleIdentifier 61 | ${info.macConfig.appId} 62 | CFBundleDisplayName 63 | ${info.displayName} 64 | CFBundleInfoDictionaryVersion 65 | 6.0 66 | CFBundleName 67 | ${info.displayName} 68 | CFBundlePackageType 69 | ${info.macConfig.infoPlist.bundlePackageType} 70 | CFBundleShortVersionString 71 | ${info.version} 72 | CFBundleSignature 73 | ???? 74 | CFBundleVersion 75 | ${info.version} 76 | NSHumanReadableCopyright 77 | ${info.organizationName} 78 | NSHighResolutionCapable 79 | 80 | NSSupportsAutomaticGraphicsSwitching 81 | 82 | AdministratorRequired 83 | #if ($info.administratorRequired) 84 | 85 | #else 86 | 87 | #end 88 | JavaX 89 | 90 | MainClass 91 | ${info.mainClass} 92 | #if (!$info.bundleJre && $info.jreMinVersion) 93 | JVMVersion 94 | ${info.jreMinVersion}+ 95 | #end 96 | ClassPath 97 | 98 | #foreach ($classpath in $info.classpaths) 99 | $classpath 100 | #end 101 | 102 | #if (!$info.vmArgs.empty) 103 | VMOptions 104 | 105 | #foreach ($vmArg in $info.vmArgs) 106 | $vmArg 107 | #end 108 | 109 | #end 110 | #if ($info.useResourcesAsWorkingDir) 111 | WorkingDirectory 112 | $APP_PACKAGE/Contents/Resources 113 | #end 114 | JVMOptionsFile 115 | $APP_PACKAGE/Contents/Resources/${info.name}.l4j.ini 116 | #if ($info.scripts.bootstrap) 117 | BootstrapScript 118 | $APP_PACKAGE/Contents/Resources/scripts/${info.bootstrapFile.name} 119 | #end 120 | RelocateJar 121 | #if ($info.macConfig.relocateJar) 122 | 123 | #else 124 | 125 | #end 126 | 127 | LSEnvironment 128 | 129 | #if ($info.bundleJre) 130 | JAVA_HOME 131 | Contents/PlugIns/${info.jreDirectoryName}.jre/Contents/Home 132 | #end 133 | #if($info.envPath) 134 | PATH 135 | ${info.envPath} 136 | #end 137 | 138 | NSAppleEventsUsageDescription 139 | There was an error while launching the application. Please 140 | click OK to display a dialog with more information or cancel and view 141 | the syslog for details. 142 | #if($info.macConfig.infoPlist && $info.macConfig.infoPlist.additionalEntries) 143 | ${info.macConfig.infoPlist.additionalEntries} 144 | #end 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/main/resources/mac/RuntimeInfo.plist.vtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | 9 | CFBundleIdentifier 10 | ${info.macConfig.appId}.runtime.java 11 | CFBundleInfoDictionaryVersion 12 | 7.0 13 | CFBundleName 14 | Java Runtime Image 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | ${info.version} 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${info.version} 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/mac/assembly.xml.vtl: -------------------------------------------------------------------------------- 1 | 2 | 6 | bundle 7 | false 8 | 9 | 10 | ${info.name}.app 11 | ${info.outputDirectory}/${info.name}/${info.name}.app 12 | 13 | Contents/MacOS/${info.executable.name} 14 | Contents/MacOS/universalJavaApplicationStub 15 | #if ($info.bundleJre) 16 | Contents/PlugIns/${info.jreDirectoryName}/Contents/Home/bin/* 17 | Contents/PlugIns/${info.jreDirectoryName}/Contents/Home/lib/jspawnhelper 18 | #end 19 | #if ($info.scripts.bootstrap) 20 | Contents/Resources/scripts/* 21 | #end 22 | 23 | 24 | 25 | ${info.name}.app 26 | ${info.outputDirectory}/${info.name}/${info.name}.app 27 | 28 | Contents/MacOS/${info.executable.name} 29 | Contents/MacOS/universalJavaApplicationStub 30 | #if ($info.bundleJre) 31 | Contents/PlugIns/${info.jreDirectoryName}/Contents/Home/bin/* 32 | Contents/PlugIns/${info.jreDirectoryName}/Contents/Home/lib/jspawnhelper 33 | #end 34 | #if ($info.scripts.bootstrap) 35 | Contents/Resources/scripts/* 36 | #end 37 | 38 | 0755 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/resources/mac/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/mac/background.png -------------------------------------------------------------------------------- /src/main/resources/mac/customize-dmg.applescript.vtl: -------------------------------------------------------------------------------- 1 | -- credits: https://github.com/create-dmg/create-dmg 2 | on run (volumeName) 3 | tell application "Finder" 4 | tell disk (volumeName as string) 5 | open 6 | 7 | set theXOrigin to ${info.macConfig.windowX} 8 | set theYOrigin to ${info.macConfig.windowY} 9 | set theWidth to ${info.macConfig.windowWidth} 10 | set theHeight to ${info.macConfig.windowHeight} 11 | 12 | set theBottomRightX to (theXOrigin + theWidth) 13 | set theBottomRightY to (theYOrigin + theHeight) 14 | set dsStore to "\"/Volumes/" & volumeName & "/.DS_STORE\"" 15 | 16 | tell container window 17 | set current view to icon view 18 | set toolbar visible to false 19 | set statusbar visible to false 20 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} 21 | set position of every item to {theBottomRightX + 100, 100} 22 | end tell 23 | 24 | set opts to the icon view options of container window 25 | tell opts 26 | set icon size to ${info.macConfig.iconSize} 27 | set text size to ${info.macConfig.textSize} 28 | set arrangement to not arranged 29 | end tell 30 | set background picture of opts to file ".background:background.png" 31 | 32 | -- Positioning 33 | set position of item "${info.appFile.name}" to {${info.macConfig.iconX}, ${info.macConfig.iconY}} 34 | 35 | -- Hiding 36 | set the extension hidden of item "${info.appFile.name}" to true 37 | 38 | -- Application 39 | set position of item "Applications" to {${info.macConfig.appsLinkIconX}, ${info.macConfig.appsLinkIconY}} 40 | 41 | close 42 | open 43 | -- Force saving of the size 44 | delay 1 45 | 46 | tell container window 47 | set statusbar visible to false 48 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10} 49 | end tell 50 | end tell 51 | 52 | delay 1 53 | 54 | tell disk (volumeName as string) 55 | tell container window 56 | set statusbar visible to false 57 | set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} 58 | end tell 59 | end tell 60 | 61 | --give the finder some time to write the .DS_Store file 62 | delay 3 63 | 64 | set waitTime to 0 65 | set ejectMe to false 66 | repeat while ejectMe is false 67 | delay 1 68 | set waitTime to waitTime + 1 69 | if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true 70 | end repeat 71 | log "waited " & waitTime & " seconds for .DS_STORE to be created." 72 | end tell 73 | end run -------------------------------------------------------------------------------- /src/main/resources/mac/default-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/mac/default-icon.icns -------------------------------------------------------------------------------- /src/main/resources/mac/entitlements.plist.vtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | com.apple.security.cs.allow-jit 10 | 11 | 15 | com.apple.security.cs.allow-unsigned-executable-memory 16 | 17 | 21 | com.apple.security.cs.disable-executable-page-protection 22 | 23 | 27 | com.apple.security.cs.allow-dyld-environment-variables 28 | 29 | 33 | com.apple.security.cs.disable-library-validation 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/resources/mac/startup.vtl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # MacOS startup script generated by JavaPackager plugin 3 | SCRIPTPATH=`cd "$(dirname "$0")" ; pwd` 4 | osascript -e "do shell script quoted form of \"$SCRIPTPATH\" & \"/universalJavaApplicationStub $@\" with administrator privileges" & -------------------------------------------------------------------------------- /src/main/resources/mac/universalJavaApplicationStub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/mac/universalJavaApplicationStub -------------------------------------------------------------------------------- /src/main/resources/mac/universalJavaApplicationStub.arm64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/mac/universalJavaApplicationStub.arm64 -------------------------------------------------------------------------------- /src/main/resources/mac/universalJavaApplicationStub.x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/mac/universalJavaApplicationStub.x86_64 -------------------------------------------------------------------------------- /src/main/resources/windows/JavaLauncher.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/windows/JavaLauncher.exe -------------------------------------------------------------------------------- /src/main/resources/windows/WinRun4J.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/windows/WinRun4J.exe -------------------------------------------------------------------------------- /src/main/resources/windows/WinRun4J64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/windows/WinRun4J64.exe -------------------------------------------------------------------------------- /src/main/resources/windows/assembly.xml.vtl: -------------------------------------------------------------------------------- 1 | 2 | 6 | bundle 7 | false 8 | 9 | 10 | ${info.name} 11 | ${info.outputDirectory}/${info.name} 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/resources/windows/default-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/windows/default-icon.ico -------------------------------------------------------------------------------- /src/main/resources/windows/exe.manifest.vtl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ${info.displayName} 5 | 6 | 7 | 8 | #if($info.administratorRequired) 9 | 10 | #else 11 | 12 | #end 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/resources/windows/ini.vtl: -------------------------------------------------------------------------------- 1 | main.class=${info.mainClass} 2 | log.level=error 3 | ini.override=true 4 | classpath.1=libs\*.jar 5 | classpath.2=${info.jarFile.name} 6 | #set ($classpathCounter=3) 7 | #foreach ($cp in $info.classpaths) 8 | classpath.${classpathCounter}=$!{cp} 9 | #set ($classpathCounter=$classpathCounter+1) 10 | #end 11 | #if ($info.bundleJre) 12 | vm.location=${info.jreDirectoryName}\\${info.winConfig.vmLocation} 13 | #else 14 | vm.location=%JAVA_HOME%\bin\client\jvm.dll|%JAVA_HOME%\bin\server\jvm.dll 15 | #end 16 | #if (!$info.bundleJre && $info.jreMinVersion) 17 | vm.version.min=${info.jreMinVersion} 18 | #end 19 | #if ($info.useResourcesAsWorkingDir) 20 | working.directory=. 21 | #end 22 | #set ($vmArgsCounter=1) 23 | #foreach ($vmArg in $info.vmArgs) 24 | vmarg.${vmArgsCounter}=$!{vmArg} 25 | #set ($vmArgsCounter=$vmArgsCounter+1) 26 | #end 27 | 28 | [ErrorMessages] 29 | java.not.found=A suitable version of Java could not be found on your system. Please contact ${info.organizationName}. 30 | java.failed=Java failed to startup successfully. Please contact ${info.organizationName}. 31 | -------------------------------------------------------------------------------- /src/main/resources/windows/iss.vtl: -------------------------------------------------------------------------------- 1 | \#define MyAppName "${info.name}" 2 | \#define MyAppVersion "${info.version}" 3 | \#define MyAppPublisher "${info.organizationName}" 4 | \#define MyAppURL "$!{info.organizationUrl}" 5 | \#define MyAppExeName "${info.executable.name}" 6 | \#define MyAppFolder "${info.name}" 7 | \#define MyAppLicense "$!{info.licenseFile.absolutePath}" 8 | \#define MyAppIcon "${info.iconFile.absolutePath}" 9 | \#define MyShortcutName = "${info.winConfig.shortcutName}" 10 | 11 | [Setup] 12 | AppId={{{#MyAppName}}} 13 | AppName={#MyAppName} 14 | AppVersion={#MyAppVersion} 15 | AppVerName={#MyAppName} {#MyAppVersion} 16 | AppPublisher={#MyAppPublisher} 17 | AppPublisherURL={#MyAppURL} 18 | AppSupportURL={#MyAppURL} 19 | AppUpdatesURL={#MyAppURL} 20 | DefaultDirName={autopf}\{#MyAppFolder} 21 | #if ($info.winConfig.disableDirPage) 22 | DisableDirPage=yes 23 | #else 24 | DisableDirPage=no 25 | #end 26 | #if ($info.winConfig.disableProgramGroupPage) 27 | DisableProgramGroupPage=yes 28 | #else 29 | DefaultGroupName={#MyAppName} 30 | DisableProgramGroupPage=no 31 | #end 32 | #if ($info.winConfig.disableFinishedPage) 33 | DisableFinishedPage=yes 34 | #else 35 | DisableFinishedPage=no 36 | #end 37 | #if ($info.winConfig.disableWelcomePage) 38 | DisableWelcomePage=yes 39 | #else 40 | DisableWelcomePage=no 41 | #end 42 | #if ($info.winConfig.setupMode.name() == "installForAllUsers") 43 | PrivilegesRequired=admin 44 | PrivilegesRequiredOverridesAllowed=commandline 45 | #elseif ($info.winConfig.setupMode.name() == "installForCurrentUser") 46 | PrivilegesRequired=lowest 47 | PrivilegesRequiredOverridesAllowed=commandline 48 | #else 49 | PrivilegesRequiredOverridesAllowed=commandline dialog 50 | #end 51 | LicenseFile={#MyAppLicense} 52 | SetupIconFile={#MyAppIcon} 53 | UninstallDisplayIcon={app}\{#MyAppExeName} 54 | Compression=lzma 55 | SolidCompression=yes 56 | ArchitecturesInstallIn64BitMode=x64 57 | 58 | [Languages] 59 | #foreach ($language in $info.winConfig.setupLanguages.entrySet()) 60 | Name: "${language.key}"; MessagesFile: "${language.value}" 61 | #end 62 | 63 | [Tasks] 64 | #if ($info.winConfig.createDesktopIconTask) 65 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 66 | #end 67 | 68 | [Registry] 69 | #if ($info.winConfig.registry) 70 | #foreach ($entry in $info.winConfig.registry.entries) 71 | Root: ${entry.root}; Subkey: "${entry.subkey}"; ValueType: ${entry.valueTypeAsInnoSetupString}; ValueName: "$!{entry.valueName}"; ValueData: "$!{entry.valueData}"; Flags: uninsdeletevalue 72 | #end 73 | #end 74 | #foreach ($fileAssociation in $info.fileAssociations) 75 | ; ${fileAssociation.extension} extension file association 76 | Root: HKA; Subkey: "Software\Classes\.${fileAssociation.extension}\OpenWithProgids"; ValueType: string; ValueName: "${info.name}.${fileAssociation.extension}"; ValueData: ""; Flags: uninsdeletevalue 77 | Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}"; ValueType: string; ValueName: ""; ValueData: "${fileAssociation.description}"; Flags: uninsdeletekey 78 | Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\\${info.executable.name},0"; Flags: uninsdeletevalue 79 | Root: HKA; Subkey: "Software\Classes\\${info.name}.${fileAssociation.extension}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\\${info.executable.name}"" ""%1"""; Flags: uninsdeletevalue 80 | Root: HKA; Subkey: "Software\Classes\Applications\\${info.executable.name}\SupportedTypes"; ValueType: string; ValueName: ".${fileAssociation.extension}"; ValueData: ""; Flags: uninsdeletevalue 81 | #end 82 | 83 | [Files] 84 | Source: "${info.appFolder}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 85 | 86 | [Icons] 87 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\\${info.executable.name}" 88 | #if ($info.winConfig.createDesktopIconTask) 89 | Name: "{autodesktop}\{#MyShortcutName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\\${info.executable.name}"; Tasks: desktopicon 90 | #end 91 | 92 | [Run] 93 | #if (!$info.winConfig.disableRunAfterInstall) 94 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser 95 | #end 96 | 97 | [Code] 98 | 99 | function GetInstallLocation(): String; 100 | var 101 | unInstPath: String; 102 | installLocation: String; 103 | begin 104 | unInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); 105 | installLocation := ''; 106 | if not RegQueryStringValue(HKLM, unInstPath, 'InstallLocation', installLocation) then 107 | RegQueryStringValue(HKCU, unInstPath, 'InstallLocation', installLocation); 108 | Result := RemoveQuotes(installLocation); 109 | end; 110 | 111 | #if ($info.winConfig.removeOldLibs) 112 | procedure RemoveOldLibs(); 113 | var 114 | installLocation: String; 115 | libsLocation: String; 116 | begin 117 | installLocation := GetInstallLocation(); 118 | if installLocation <> '' then 119 | begin 120 | libsLocation := installLocation + '${info.libsFolder.name}'; 121 | DelTree(libsLocation, True, True, True); 122 | end; 123 | end; 124 | #end 125 | 126 | procedure CurStepChanged(CurStep: TSetupStep); 127 | begin 128 | if CurStep = ssInstall then 129 | begin 130 | #if ($info.winConfig.removeOldLibs) 131 | RemoveOldLibs(); 132 | #end 133 | end; 134 | end; 135 | -------------------------------------------------------------------------------- /src/main/resources/windows/msm.wxs.vtl: -------------------------------------------------------------------------------- 1 | #set ($name = $info.name.replaceAll("[^A-Za-z0-9_.]", "_")) 2 | #if ($info.arch.toMsiArchitecture() == "arm64") 3 | #set ($installedVersion = "500") 4 | #else 5 | #set ($installedVersion = "200") 6 | #end 7 | #set ($id = 0) 8 | #macro(list $file) 9 | #set($guid = $GUID.randomUUID()) 10 | #set($id = $id + 1) 11 | #if($file.isDirectory()) 12 | #if($file.equals(${info.appFolder})) 13 | 14 | #else 15 | 16 | #end 17 | #foreach($child in $file.listFiles()) 18 | #list($child) 19 | #end 20 | 21 | #else 22 | 23 | #if($file.equals(${info.executable})) 24 | 25 | 26 | 27 | 28 | 29 | #else 30 | 31 | #end 32 | 33 | #end 34 | #end 35 | 36 | 37 | 38 | 39 | 40 | #list(${info.appFolder}) 41 | 42 | #if ($info.winConfig.registry) 43 | 44 | #foreach ($entry in $info.winConfig.registry.entries) 45 | 46 | 55 | 56 | #end 57 | 58 | #end 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/resources/windows/rcedit-x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javapackager/JavaPackager/94db2a7974b3f9b8a11f2be280ff5472391a2e24/src/main/resources/windows/rcedit-x64.exe -------------------------------------------------------------------------------- /src/main/resources/windows/startup.vbs.vtl: -------------------------------------------------------------------------------- 1 | ' Windows startup script generated by JavaPackager plugin 2 | 3 | ' Runs an executable file (exe, bat, cmd, ...) 4 | Function Run(file) 5 | If FileExists(file) Then 6 | Set WshShell = CreateObject("WScript.Shell") 7 | WshShell.Run chr(34) & file & Chr(34), 0 8 | Set WshShell = Nothing 9 | End If 10 | End Function 11 | 12 | ' Checks if a file exists 13 | Function FileExists(FilePath) 14 | Set fso = CreateObject("Scripting.FileSystemObject") 15 | If fso.FileExists(FilePath) Then 16 | FileExists=CBool(1) 17 | Else 18 | FileExists=CBool(0) 19 | End If 20 | End Function 21 | 22 | #if ($info.bootstrapFile) 23 | ' Runs bootstrap script if exists 24 | Run "scripts\\${info.bootstrapFile.name}" 25 | #end 26 | 27 | ' Runs app executable 28 | Run "${info.executable.name}" -------------------------------------------------------------------------------- /src/main/resources/windows/why-ini.vtl: -------------------------------------------------------------------------------- 1 | mainclass=$info.mainClass 2 | #set($classpath = $info.jarFile.name) 3 | #foreach ($cp in $info.classpaths) 4 | #set($classpath = $classpath + ";" + $cp) 5 | #end 6 | classpath=${classpath} 7 | #if ($info.bundleJre) 8 | jvm_install=${info.jreDirectoryName} 9 | allow_system_java=false 10 | allow_java_location_lookup=false 11 | check_main_class=false 12 | #end 13 | #if (!$info.bundleJre && $info.jreMinVersion) 14 | min_java=${info.jreMinVersion} 15 | #end 16 | launch_options=${info.name}.l4j.ini 17 | -------------------------------------------------------------------------------- /src/main/resources/windows/wxs.vtl: -------------------------------------------------------------------------------- 1 | #set($moduleName = $info.name.replaceAll("[^A-Za-z0-9_.]", "_") + "_Module") 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | #if($info.licenseFile) 18 | 19 | #end 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------