diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..634955e2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,111 @@
+name: Bug report
+description: Report a bug to help us improve
+labels:
+ - bug
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to file a bug. Please fill in the details below.
+
+ - type: input
+ id: version
+ attributes:
+ label: JavaPackager version
+ description: Exact version used (e.g., 1.7.5). If built from source, include the commit.
+ placeholder: e.g., 1.7.5
+ validations:
+ required: true
+
+ - type: dropdown
+ id: os
+ attributes:
+ label: Operating system
+ options:
+ - Windows
+ - macOS
+ - Linux
+ - Other
+ validations:
+ required: true
+
+ - type: input
+ id: os_version
+ attributes:
+ label: OS version
+ placeholder: e.g., Windows 11 23H2, Ubuntu 22.04, macOS 14.5
+
+ - type: dropdown
+ id: build_tool
+ attributes:
+ label: Build tool
+ options:
+ - Gradle
+ - Maven
+ - Other / N/A
+ validations:
+ required: true
+
+ - type: input
+ id: jdk
+ attributes:
+ label: JDK version
+ placeholder: e.g., Temurin 21.0.3+9, Oracle JDK 17.0.10
+ validations:
+ required: true
+
+ - type: textarea
+ id: short_description
+ attributes:
+ label: Short description
+ description: What happens in one or two sentences?
+ placeholder: Clear and concise description of the problem.
+ validations:
+ required: true
+
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to reproduce
+ description: Provide minimal, reproducible steps.
+ placeholder: |
+ 1. Step one
+ 2. Step two
+ 3. Observed result
+ render: text
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ placeholder: What did you expect to happen?
+ validations:
+ required: true
+
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual behavior
+ placeholder: What actually happens?
+ validations:
+ required: true
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Logs and output
+ description: Paste relevant output or attach files.
+ placeholder: Add logs, stack traces, console output, etc.
+ render: text
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I searched for existing related issues
+ required: true
+ - label: I can provide a minimal reproducible example if needed
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..dec06859
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Documentation
+ url: https://github.com/javapackager/JavaPackager/blob/master/README.md
+ about: Check the documentation before opening an issue.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..a19ed75b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,55 @@
+name: Feature request
+description: Suggest a new feature or improvement
+labels:
+ - enhancement
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for suggesting an improvement. Tell us the problem and your proposed solution.
+
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem to solve
+ description: What need or pain point motivates this feature?
+ placeholder: Describe the problem or use case.
+ validations:
+ required: true
+
+ - type: textarea
+ id: proposal
+ attributes:
+ label: Proposed solution
+ description: Explain how you would like it to work.
+ placeholder: Detail the proposed change and expected behavior.
+ validations:
+ required: true
+
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives considered
+ placeholder: What other options did you consider?
+
+ - type: textarea
+ id: impact
+ attributes:
+ label: Impact and scope
+ placeholder: Affected users, compatibility, risks, etc.
+
+ - type: input
+ id: version
+ attributes:
+ label: JavaPackager version (if applicable)
+ placeholder: e.g., 1.7.5
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I searched for existing similar issues
+ required: true
+ - label: I am willing to contribute a PR
+ required: false
diff --git a/.github/issue_template.md b/.github/issue_template.md
deleted file mode 100644
index c1254cc1..00000000
--- a/.github/issue_template.md
+++ /dev/null
@@ -1,42 +0,0 @@
-I'm submitting a…
-
-- [ ] bug report
-- [ ] feature request
-- [ ] other
-
-**Short description of the issue/suggestion:**
-
-
-
-**Steps to reproduce the issue/enhancement:**
-
-1. [First Step]
-2. [Second Step]
-3. [Other Steps...]
-
-**What is the expected behavior?**
-
-
-
-**What is the current behavior?**
-
-
-
-**Do you have outputs, screenshots, demos or samples which demonstrate the problem or enhancement?**
-
-
-
-**What is the motivation / use case for changing the behavior?**
-
-
-
-**Please tell us about your environment:**
-
-- JavaPackager version:
-- OS version:
-- JDK version:
-- Build tool:
- - [ ] Maven
- - [ ] Gradle
-
-**Other information** (e.g. related issues, suggestions how to fix, links for us to have context)
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 93fc4d56..9c64626e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,26 @@ target
.project
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
-!gradle-wrapper.jar
\ No newline at end of file
+!gradle-wrapper.jar
+src/main/resources/windows/winrun4j-launcher.jar
+
+# Log file
+*.log
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# IntelliJ
+**/.idea/
+**.iml
+
+# Eclipse
+**/.settings/
+
+# Maven
+**/target/
+**/*.xml.releaseBackup
+/release.properties
+
+## More
+.DS_Store
diff --git a/README.md b/README.md
index 83add52f..b4ddf145 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,31 @@
# JavaPackager
-[](https://search.maven.org/artifact/io.github.fvarrui/javapackager)
+[](https://central.sonatype.com/search?smo=true&q=a%3Ajavapackager+g%3Aio.github.fvarrui)
[](https://www.gnu.org/licenses/gpl-3.0.html)
-JavaPackager is a hybrid plugin for **Maven** and **Gradle** which provides an easy way to package Java applications in native Windows, Mac OS X or GNU/Linux executables, and generate installers for them.
+JavaPackager is a hybrid plugin for **Maven** and **Gradle** which provides an easy way to package Java applications in native Windows, MacOS or GNU/Linux executables, and generate installers for them.
-> SNAPSHOT version is not released to Maven Central, so you have to [install it manually](#how-to-build-and-install-the-plugin).
+> [!IMPORTANT]
+> See [JavaPackager changes and fixes](https://github.com/fvarrui/JavaPackager/releases).
-> :eyes: See [JavaPackager changes and fixes](https://github.com/fvarrui/JavaPackager/releases).
+## Project maintainers needed
+
+This project has been actively maintained for many years, but due to lack of time, I can no longer dedicate the attention it deserves. To keep it alive and evolving, I’m looking for contributors to help with its maintenance.
+
+:point_right: **[Join the discussion](https://github.com/fvarrui/JavaPackager/discussions/460)**
+
+Any help is welcome! Thank you for your support.
## History
-It was born while teaching to my students how to build and distribute their Java apps, and after seeing that a chain of several plugins was needed to achieve this task, I decided to develop a plugin :ring: to govern them all.
+It was born while teaching to my students how to build and distribute their Java apps, and after seeing that a chain of several plugins was needed to achieve this task, I decided to develop a plugin :ring: to rule them all.
+
+## Apps packaged with JavaPackager
+
+- [AstroImageJ](http://astroimagej.com/)
+- [Astro Pixel Processor](https://www.astropixelprocessor.com/)
+- [GistFX](https://github.com/RedmondSims/GistFX)
+- [Spektar Design Lab](https://spektar.io/)
## How to use this plugin
@@ -23,7 +37,7 @@ Add the following `plugin` tag to your `pom.xml`:
io.github.fvarrui
javapackager
- 1.6.5
+ {latest.version}
package
@@ -53,6 +67,7 @@ Add the following `plugin` tag to your `pom.xml`:
```
+> [!tip]
> See [Maven plugin configuration samples](docs/maven/plugin-configuration-samples.md) to know more.
And execute the next command in project's root folder:
@@ -71,7 +86,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'io.github.fvarrui:javapackager:1.6.5'
+ classpath 'io.github.fvarrui:javapackager:{latest.version}'
}
}
@@ -88,7 +103,7 @@ task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, depen
bundleJre = true|false
generateInstaller = true|false
administratorRequired = true|false
- platform = auto|linux|mac|windows
+ platform = "auto"|"linux"|"mac"|"windows"
additionalResources = [ file('file path'), file('folder path'), ... ]
linuxConfig {
...
@@ -103,7 +118,8 @@ task packageMyApp(type: io.github.fvarrui.javapackager.gradle.PackageTask, depen
}
```
-> See [Gradle plugin configuration samples](docs/gradle/plugin-configuration-samples.md) to know more.
+> [!TIP]
+> See [Gradle plugin configuration samples](docs/gradle/plugin-configuration-samples.md) to know more.
And execute the next command in project's root folder:
@@ -115,80 +131,87 @@ gradle packageMyApp
By default it will generate next artifacts in `${outputDirectory} ` folder:
-| Artifact | Description | Platform | Requires |
-| --------------------------------------- | ---------------------------------------------------------------- | -------- | -------------------------------------------------- |
-| `${name}` | Directory with native application and other assets. | All | |
-| `${name}-${version}-runnable.jar` | Runnable JAR file. | All | |
-| `${name}_${version}.deb` | DEB package file. | All | |
-| `${name}_${version}.rpm` | RPM package file. | All | |
-| `${name}_${version}.exe` | Setup file. | Windows | [Inno Setup](http://www.jrsoftware.org/isinfo.php) |
-| `${name}_${version}.msi` | MSI installer file. | Windows | [WiX Toolset](https://wixtoolset.org/) |
-| `${name}_${version}.msm` | MSI merge module file. | Windows | [WiX Toolset](https://wixtoolset.org/) |
-| `${name}_${version}.dmg` | Disk image file (uses **hdiutil**). | Mac OS | |
-| `${name}_${version}.pkg` | PKG installer file (uses **pkgbuild**). | Mac OS | |
-| `${name}-${version}-${platform}.zip` | Zipball containing generated directory `${name}`. | All | |
-| `${name}-${version}-${platform}.tar.gz` | Compressed tarball containing generated directory `${name}`. | All | |
-| `assets` | Directory with all intermediate files generated by JavaPackager. | All | |
-
+| Artifact | Description | Platform | Requires |
+| --------------------------------------- | ---------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
+| `${name}` | Directory with native application and other assets. | All | |
+| `${name}-${version}-runnable.jar` | Runnable JAR file. | All | |
+| `${name}_${version}.AppImage` | AppImage package file. | GNU/Linux | [FUSE 2](https://github.com/AppImage/AppImageKit/wiki/FUSE) to run the app. |
+| `${name}_${version}.deb` | DEB package file. | All | |
+| `${name}_${version}.rpm` | RPM package file. | All | |
+| `${name}_${version}.exe` | Setup file. | Windows | [Inno Setup](http://www.jrsoftware.org/isinfo.php)Â (`iscc`Â command must be in PATH variable) |
+| `${name}_${version}.msi` | MSI installer file. | Windows | [WiX Toolset](https://wixtoolset.org/)Â (`candle`Â and `light` commands must be in PATH variable) |
+| `${name}_${version}.msm` | MSI merge module file. | Windows | [WiX Toolset](https://wixtoolset.org/)Â (Â `candle`Â and `light` commands must be in PATH variable) |
+| `${name}_${version}.dmg` | Disk image file (uses **hdiutil**). | MacOS | |
+| `${name}_${version}.pkg` | PKG installer file (uses **pkgbuild**). | MacOS | |
+| `${name}-${version}-${platform}.zip` | Zipball containing generated directory `${name}`. | All | |
+| `${name}-${version}-${platform}.tar.gz` | Compressed tarball containing generated directory `${name}`. | All | |
+| `assets` | Directory with all intermediate files generated by JavaPackager. | All | |
+
+> [!TIP]
> **Inno Setup** and **WiX Toolset** installation [guide](docs/windows-tools-guide.md).
### Plugin configuration properties
-| Property | Mandatory | Default value | Description |
-| -------------------------- | ------------------ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `additionalModulePaths` | :x: | `[]` | Additional module paths for `jdeps`. |
-| `additionalModules` | :x: | `[]` | Additional modules to the ones identified by `jdeps` or the specified with `modules` property. |
-| `additionalResources` | :x: | `[]` | Additional files and folders to include in the bundled app. |
-| `administratorRequired` | :x: | `false` | App will run as administrator (with elevated privileges). |
-| `assetsDir` | :x: | `${basedir}/assets` or `${projectdir}/assets` | Assets location (icons and custom Velocity templates). |
-| `bundleJre` | :x: | `false` | Embeds a customized JRE with the app. |
-| `classpath` | :x: | | List of additional paths to JVM classpath, separated with `;` (recommended) or `:`. |
-| `copyDependencies` | :x: | `true` | Bundles all dependencies (JAR files) with the app. |
-| `createTarball` | :x: | `false` | Bundles app folder in tarball. |
-| `createZipball` | :x: | `false` | Bundles app folder in zipball. |
-| `customizedJre` | :x: | `true` | Generates a customized JRE, including only identified or specified modules. Otherwise, all modules will be included. |
-| `description` | :x: | `${project.description}` or `${displayName}` | Project description. |
-| `displayName` | :x: | `${project.name}` or `${name}` | App name to show. |
-| `envPath` | :x: | | Defines PATH environment variable in GNU/Linux and Mac OS X startup scripts. |
-| `extra` | :x: | | Map with extra properties to be used in customized Velocity templates, accesible through `$info.extra` variable. |
-| `generateInstaller` | :x: | `true` | Generates an installer for the app. |
-| `jdkPath` | :x: | `${java.home}` | JDK used to generate a customized JRE. It allows to bundle customized JREs for different platforms. |
-| `jreDirectoryName` | :x: | `"jre"` | Bundled JRE directory name. |
-| `jreMinVersion` | :x: | | JRE minimum version. If an appropriate version cannot be found display error message. Disabled if a JRE is bundled. |
-| `jrePath` | :x: | `""` | Path to JRE folder. If specified, it will bundle this JRE with the app, and won't generate a customized JRE. For Java 8 version or least. |
-| `licenseFile` | :x: | `${project.licenses[0].url}` or `${basedir}/LICENSE` or `${projectdir}/LICENSE` | Path to project license file. |
-| `mainClass` | :heavy_check_mark: | `${exec.mainClass}` | Full path to your app main class. |
-| `manifest` | :x: | | [Allows adding additional entries to MANIFEST.MF file.](docs/manifest.md) |
-| `modules` | :x: | `[]` | Modules to customize the bundled JRE. Don't use `jdeps` to get module dependencies. |
-| `name` | :x: | `${project.name}` or `${project.artifactId}` | App name. |
-| `organizationName` | :x: | `${project.organization.name}` or `"ACME"` | Organization name. |
-| `organizationUrl` | :x: | `${project.organization.url}` | Organization website URL. |
-| `organizationEmail` | :x: | | Organization email. |
-| `outputDirectory` | :x: | `${project.build.directory}` or `${project.builddir}` | Output directory (where the artifacts will be generated). |
-| `packagingJdk` | :x: | `${java.home}` | JDK used in the execution of `jlink` and other JDK tools. |
-| `platform` | :x: | `auto` | Defines the target platform, which could be different to the execution platform. Possible values: `auto`, `mac`, `linux`, `windows`. Use `auto` for using execution platform as target. |
-| `runnableJar` | :x: | | Defines your own JAR file to be bundled. If it's ommited, the plugin packages your code in a runnable JAR and bundle it with the app. |
-| `scripts` | :x: | | Specify bootstrap script. **Pre and post-install scripts comming soon!** |
-| `url` | :x: | | App website URL. |
-| `useResourcesAsWorkingDir` | :x: | `true` | Uses app resources folder as default working directory (always `true` on Mac OS). |
-| `version` | :x: | `${project.version}` | App version. |
-| `vmArgs` | :x: | `[]` | VM arguments. |
-
+| Property | Mandatory | Default value | Description |
+| -------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `additionalModulePaths` | :x: | `[]` | Additional module paths for `jdeps`. |
+| `additionalModules` | :x: | `[]` | Additional modules to the ones identified by `jdeps` or the specified with `modules` property. |
+| `additionalResources` | :x: | `[]` | Additional files and folders to include in the bundled app. |
+| `arch` | :x: | `${os.arch}` | The dependency of some ArtifactGenerator objects in the process of making packages, such as GenerateDeb |
+| `administratorRequired` | :x: | `false` | App will run as administrator (with elevated privileges). |
+| `assetsDir` | :x: | `${basedir}/assets` or `${projectdir}/assets` | Assets location (icons and custom Velocity templates). |
+| `bundleJre` | :x: | `false` | Embeds a customized JRE with the app. |
+| `classpath` | :x: | | List of additional paths to JVM classpath, separated with `;` (recommended) or `:`. |
+| `copyDependencies` | :x: | `true` | Bundles all dependencies (JAR files) with the app. |
+| `createTarball` | :x: | `false` | Bundles app folder in tarball. |
+| `createZipball` | :x: | `false` | Bundles app folder in zipball. |
+| `customizedJre` | :x: | `true` | Generates a customized JRE, including only identified or specified modules. Otherwise, all modules will be included. |
+| `description` | :x: | `${project.description}` or `${displayName}` | Project description. |
+| `displayName` | :x: | `${project.name}` or `${name}` | App name to show. |
+| `envPath` | :x: | | Defines PATH environment variable in GNU/Linux and MacOS startup scripts. |
+| `extra` | :x: | | Map with extra properties to be used in customized Velocity templates, accesible through `$info.extra` variable. |
+| `fileAssociations` | :x: | [`FileAssociation[]`](https://github.com/fvarrui/JavaPackager/blob/master/src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java) | Associate file extensions or MIME types to the app. |
+| `forceInstaller` | :x: | `false` | If `true`, skips operating system check when generating installers. |
+| `generateInstaller` | :x: | `true` | Generates an installer for the app. |
+| `jdkPath` | :x: | `${java.home}` | JDK used to generate a customized JRE. It allows to bundle customized JREs for different platforms. |
+| `jreDirectoryName` | :x: | `"jre"` | Bundled JRE directory name. |
+| `jreMinVersion` | :x: | | JRE minimum version. If an appropriate version cannot be found display error message. Disabled if a JRE is bundled. |
+| `jrePath` | :x: | `""` | Path to JRE folder. If specified, it will bundle this JRE with the app, and won't generate a customized JRE. For Java 8 version or least. |
+| `licenseFile` | :x: | `${project.licenses[0].url}` or `${basedir}/LICENSE` or `${projectdir}/LICENSE` | Path to project license file. |
+| `mainClass` | :heavy_check_mark: | `${exec.mainClass}` | Full path to your app main class. |
+| `manifest` | :x: | | [Allows adding additional entries to MANIFEST.MF file.](docs/manifest.md) |
+| `modules` | :x: | `[]` | Modules to customize the bundled JRE. Don't use `jdeps` to get module dependencies. |
+| `name` | :x: | `${project.name}` or `${project.artifactId}` | App name. |
+| `organizationName` | :x: | `${project.organization.name}` or `"ACME"` | Organization name. |
+| `organizationUrl` | :x: | `${project.organization.url}` | Organization website URL. |
+| `organizationEmail` | :x: | | Organization email. |
+| `outputDirectory` | :x: | `${project.build.directory}` or `${project.builddir}` | Output directory (where the artifacts will be generated). |
+| `packagingJdk` | :x: | `${java.home}` | JDK used in the execution of `jlink` and other JDK tools. |
+| `platform` | :x: | `auto` | Defines the target platform, which could be different to the execution platform. Possible values: `auto`, `mac`, `linux`, `windows`. Use `auto` for using execution platform as target. |
+| `runnableJar` | :x: | | Defines your own JAR file to be bundled. If it's ommited, the plugin packages your code in a runnable JAR and bundle it with the app. |
+| `scripts` | :x: | | Specify bootstrap script. **Pre and post-install scripts comming soon!** |
+| `url` | :x: | | App website URL. |
+| `useResourcesAsWorkingDir` | :x: | `true` | Uses app resources folder as default working directory (always `true` on MacOS). |
+| `version` | :x: | `${project.version}` | App version. |
+| `vmArgs` | :x: | `[]` | VM arguments. |
+
+> [!IMPORTANT]
> Some default values depends on the used building tool.
**Platform specific properties**
-| Property | Mandatory | Default | Description |
-| ------------- | --------- | ------- | ------------------------------------------------------------------- |
-| `linuxConfig` | :x: | | [GNU/Linux specific properties](docs/linux-specific-properties.md). |
-| `macConfig` | :x: | | [Mac OS X specific properties](docs/macosx-specific-properties.md). |
-| `winConfig` | :x: | | [Windows specific properties](docs/windows-specific-properties.md). |
+| Property | Mandatory | Description |
+| ------------- | --------- | ------------------------------------------------------------------- |
+| `linuxConfig` | :x: | [GNU/Linux specific properties](docs/linux-specific-properties.md). |
+| `macConfig` | :x: | [MacOS specific properties](docs/macosx-specific-properties.md). |
+| `winConfig` | :x: | [Windows specific properties](docs/windows-specific-properties.md). |
-> :warning: Be careful when using the `platform` property if your project uses platform dependent libraries, so the libraries of the current platform will be copied, not those required for the target platform. You can solve this problem using `classifiers`.
+> [!WARNING]
+> Be careful when using the `platform` property if your project uses platform dependent libraries, so the libraries of the current platform will be copied, not those required for the target platform. You can solve this problem using `classifiers`.
### Plugin assets
-Some assets, such as application icons and templates, could be placed in `${assetsDir}` folder organized by platform.
+Any [asset used by JavaPackager](https://github.com/fvarrui/JavaPackager/tree/master/src/main/resources), such as application icons or templates, can be replaced just by placing a file with the same name in `${assetsDir}` folder organized by platform.
```bash
${assetsDir}/
@@ -206,12 +229,13 @@ ${assetsDir}/
├── linux/
│ └── ${name}.png # on GNU/Linux it has to be a PNG file
├── mac/
-│ └── ${name}.icns # on Mac OS X it has to be a ICNS file
+│ └── ${name}.icns # on MacOS it has to be a ICNS file
└── windows/
└── ${name}.ico # on Windows it has to be a ICO file
```
-> :warning: If icon is not specified , it will use an [icon by default](https://raw.githubusercontent.com/fvarrui/JavaPackager/master/src/main/resources/linux/default-icon.png) for all platforms.
+> [!WARNING]
+> If icon is not specified , it will use an [icon by default](https://raw.githubusercontent.com/fvarrui/JavaPackager/master/src/main/resources/linux/default-icon.png) for all platforms.
#### Templates
@@ -225,9 +249,11 @@ ${assetsDir}/
| ├── assembly.xml.vtl # maven-assembly-plugin template to generate ZIP/TGZ bundles for GNU/Linux
| ├── control.vtl # DEB control template
| ├── desktop.vtl # Desktop template
+| ├── desktop-appimage.vtl # AppImage format Desktop template
+| ├── mime.xml.vtl # MIME.XML template
│ └── startup.sh.vtl # Startup script template
├── mac/
-| ├── assembly.xml.vtl # maven-assembly-plugin template to generate ZIP/TGZ bundles for Mac OS X
+| ├── assembly.xml.vtl # maven-assembly-plugin template to generate ZIP/TGZ bundles for MacOS
| ├── customize-dmg.applescript.vtl # DMG customization Applescript template
| ├── Info.plist.vtl # Info.plist template
│ └── startup.vtl # Startup script template
@@ -238,6 +264,7 @@ ${assetsDir}/
├── iss.vtl # Inno Setup Script template
├── msm.wxs.vtl # WiX Toolset WXS template to generate Merge Module
├── startup.vbs.vtl # Startup script template (VB Script)
+ ├── why-ini.vtl # WHY INI template
└── wxs.vtl # WiX Toolset WXS template to generate MSI
```
@@ -247,9 +274,9 @@ You can use [default templates](https://github.com/fvarrui/JavaPackager/tree/mas
### Additional JVM options at runtime
-When you build your app, all configuration details are hardcoded into the executable and cannot be changed without recreating it or hacking with a resource editor. JavaPackager introduces a feature that allows to pass additional JVM options at runtime from an `.l4j.ini` file (like [Launch4j](http://launch4j.sourceforge.net/docs.html) does, but available for all platforms in the same way). So, you can specify these options in the packager's configuration (packaging time), in INI file (runtime) or in both.
+When you build your app, all configuration details are hardcoded into the executable and cannot be changed without recreating or hacking it with a resource editor. JavaPackager introduces a feature that allows to pass additional JVM options at runtime from an `.l4j.ini` file (like [Launch4j](http://launch4j.sourceforge.net/docs.html) does, but available for all platforms in the same way). So, you can specify these options in the packager's configuration (packaging time), in INI file (runtime) or in both.
-The INI file's name must correspond to `${name}.l4j.ini` and it has to be located next to the executable on Windows and GNU/Linux, and in `Resources` folder on Mac OS X.
+The INI file's name must correspond to `${name}.l4j.ini` and it has to be located next to the executable on Windows and GNU/Linux, and in `Resources` folder on MacOS.
The options should be separated with spaces or new lines:
@@ -260,7 +287,8 @@ The options should be separated with spaces or new lines:
-Xms16m
```
-> An VM argument per line.
+> [!IMPORTANT]
+> An VM argument per line.
And then bundle this file with your app:
@@ -270,55 +298,112 @@ And then bundle this file with your app:
```
+> [!NOTE]
> Last property copies `${name}.l4j.ini` file next to the EXE/binary on Windows/Linux, and in `Resources` folder on MacOS.
-## How to build and install the plugin
+## How to use SNAPSHOT versions
-> Useful to try SNAPSHOT versions.
+[Here](https://oss.sonatype.org/content/repositories/snapshots/io/github/fvarrui/javapackager/) you can find the uploaded JavaPackager SNAPSHOT versions.
-Execute next commands in BASH (GNU/Linux or macOS) or CMD (Windows):
+### Maven
-1. Download source code and change to the project directory:
+Add the plugin repository to your `pom.xml`:
-```bash
-git clone https://github.com/fvarrui/JavaPackager.git [--branch x]
-cd JavaPackager
+```xml
+
+
+ nexus
+ nexus-snapshot-repository
+ https://oss.sonatype.org/content/repositories/snapshots
+
+ true
+ always
+
+
+ false
+
+
+
```
-> where `x` is branch name (e.g. `devel`)
+And then you can use the latest SNAPSHOT version:
-2. Compile, package and install the plugin in your local repository (ommit `./` on Windows):
+```xml
+
+ io.github.fvarrui
+ javapackager
+ {javapackager.version}-SNAPSHOT
+ [...]
+
+```
-```bash
-./gradlew publishToMavenLocal
+Or a specific SNAPSHOT version (specifying its timestamp and index):
+
+```xml
+
+ io.github.fvarrui
+ javapackager
+ {javapackager.version}-{timestamp}-{index}
+ [...]
+
```
-## How to release the plugin to Maven Central
+> SNAPSHOT version example: `1.7.2-20230505.095442-5`.
-Run next command (ommit `./` on Windows):
+### Gradle
-```bash
-./gradlew -Prelease uploadArchives closeAndReleaseRepository
+Add the plugin repository to your `build.gradle` and use the latest SNAPSHOT version:
+
+```groovy
+buildscript {
+ repositories {
+ maven {
+ url "https://oss.sonatype.org/content/repositories/snapshots"
+ }
+ }
+ dependencies {
+ classpath 'io.github.fvarrui:javapackager:{javapackager.version}-SNAPSHOT'
+ }
+}
+```
+
+Or set a specific SNAPSHOT version specifying its timestamp and index:
+
+```groovy
+buildscript {
+ [...]
+ dependencies {
+ classpath 'io.github.fvarrui:javapackager:{javapackager.version}-{timestamp}-{index}'
+ }
+}
```
-> Related [guide](https://nemerosa.ghost.io/2015/07/01/publishing-to-the-maven-central-using-gradle/).
+> SNAPSHOT version example: `1.7.2-20230505.095442-5`.
-## How to release the plugin to Gradle plugin portal
+## How to build and install the plugin in your local repo
-Only the first time, run next command:
+Execute next commands in BASH (GNU/Linux or macOS) or CMD (Windows):
+
+1. Download source code and change to the project directory:
```bash
-./gradlew login
+git clone https://github.com/fvarrui/JavaPackager.git [--branch devel]
+cd JavaPackager
```
-And then, run (ommit `./` on Windows):
+2. Compile, package and install the plugin in your local repository (ommit `./` on Windows):
```bash
-./gradlew publishPlugins
+./gradlew publishToMavenLocal
```
-> Related [guide](https://plugins.gradle.org/docs/submit).
+> [!IMPORTANT]
+> It is recommended to build the plugin with Java 19.
-## Future features
+## How to release the plugin to Maven Central
-Check the [TO-DO list](https://github.com/fvarrui/JavaPackager/projects/1#column-7704117) to know the features we plan to add to JavaPackager.
+Run next command (ommit `./` on Windows):
+
+```bash
+./gradlew publish closeAndReleaseRepository
+```
diff --git a/build.gradle b/build.gradle
index d4be4f54..454b9f65 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
plugins {
- id 'java'
- id 'maven'
- id 'maven-publish'
- id 'java-gradle-plugin'
- id 'com.gradle.plugin-publish' version '0.12.0'
- id 'io.codearte.nexus-staging' version '0.21.2'
- id 'eclipse'
- id 'de.undercouch.download' version '4.1.1'
+ id 'java'
+ id 'maven-publish'
+ id 'java-gradle-plugin'
+ id 'com.gradle.plugin-publish' version '1.1.0'
+ id 'io.codearte.nexus-staging' version '0.21.2'
+ id 'eclipse'
+ id 'de.undercouch.download' version '5.0.4'
+ id 'signing'
}
repositories {
@@ -18,59 +18,53 @@ repositories {
}
gradlePlugin {
- plugins {
- javaPackagerPlugin {
- id = 'io.github.fvarrui.javapackager.plugin'
- implementationClass = 'io.github.fvarrui.javapackager.gradle.PackagePlugin'
- }
- }
-}
-
-pluginBundle {
website = 'http://github.com/fvarrui/JavaPackager'
vcsUrl = 'http://github.com/fvarrui/JavaPackager.git'
- description = 'Packages Java applications as native Windows, Mac OS X or GNU/Linux executables and creates installers for them'
- tags = ['java', 'packager', 'gradle-plugin', 'maven-plugin', 'native', 'installer', 'debian-packages', 'rpm-packages', 'dmg', 'maven', 'gradle', 'distribution', 'javapackager', 'linux-executables', 'deb', 'rpm', 'native-windows', 'java-applications', 'pkg', 'msi']
+ description = 'Packages Java applications as native Windows, MacOS or GNU/Linux executables and creates installers for them'
+
plugins {
- javaPackagerPlugin {
+ create("javaPackagerPlugin") {
+ id = 'io.github.fvarrui.javapackager.plugin'
displayName = 'JavaPackager'
+ implementationClass = 'io.github.fvarrui.javapackager.gradle.PackagePlugin'
+ tags.set(['java', 'packager', 'gradle-plugin', 'maven-plugin', 'native', 'installer', 'debian-packages', 'rpm-packages', 'dmg', 'maven', 'gradle', 'distribution', 'javapackager', 'linux-executables', 'deb', 'rpm', 'native-windows', 'java-applications', 'pkg', 'msi'])
}
}
}
configurations {
- mavenEmbedder
+ mavenEmbedder
}
dependencies {
- implementation 'org.apache.maven:maven-plugin-api:3.6.0'
- implementation 'org.codehaus.plexus:plexus-utils:3.1.1'
- implementation 'org.twdata.maven:mojo-executor:2.3.0'
- implementation 'commons-io:commons-io:2.6'
- implementation 'org.apache.commons:commons-lang3:3.9'
- implementation 'org.apache.commons:commons-collections4:4.1'
- implementation 'org.apache.commons:commons-compress:1.21'
- implementation 'org.apache.velocity:velocity-engine-core:2.0'
- implementation 'org.vafer:jdeb:1.9'
- implementation 'net.jsign:jsign-core:3.1'
- implementation 'org.redline-rpm:redline:1.2.10'
- implementation 'io.github.fvarrui:launch4j:2.5.2'
-
- testImplementation 'junit:junit:4.12'
-
- compileOnly 'org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0'
-
- mavenEmbedder 'org.apache.maven:maven-embedder:3.6.0'
- mavenEmbedder 'org.apache.maven:maven-compat:3.6.0'
- mavenEmbedder 'org.slf4j:slf4j-simple:1.7.30'
- mavenEmbedder 'org.eclipse.aether:aether-connector-basic:1.1.0'
- mavenEmbedder 'org.eclipse.aether:aether-transport-wagon:1.1.0'
- mavenEmbedder 'org.apache.maven.wagon:wagon-http:3.4.1'
- mavenEmbedder 'org.apache.maven.wagon:wagon-provider-api:3.4.1'
+ implementation 'org.apache.maven:maven-plugin-api:3.6.0'
+ implementation 'org.codehaus.plexus:plexus-utils:3.5.1'
+ implementation 'org.twdata.maven:mojo-executor:2.3.0'
+ implementation 'commons-io:commons-io:2.16.1'
+ implementation 'org.apache.commons:commons-lang3:3.9'
+ implementation 'org.apache.commons:commons-collections4:4.3'
+ implementation 'org.apache.commons:commons-compress:1.21'
+ implementation 'org.apache.velocity:velocity-engine-core:2.3'
+ implementation 'org.vafer:jdeb:1.9'
+ implementation 'net.jsign:jsign-core:6.0'
+ implementation 'org.redline-rpm:redline:1.2.10'
+ implementation 'edu.sc.seis.launch4j:launch4j:3.0.5'
+
+ testImplementation 'junit:junit:4.13.1'
+
+ compileOnly 'org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0'
+
+ mavenEmbedder 'org.apache.maven:maven-embedder:3.6.0'
+ mavenEmbedder 'org.apache.maven:maven-compat:3.6.0'
+ mavenEmbedder 'org.slf4j:slf4j-simple:1.7.30'
+ mavenEmbedder 'org.eclipse.aether:aether-connector-basic:1.1.0'
+ mavenEmbedder 'org.eclipse.aether:aether-transport-wagon:1.1.0'
+ mavenEmbedder 'org.apache.maven.wagon:wagon-http:3.4.1'
+ mavenEmbedder 'org.apache.maven.wagon:wagon-provider-api:3.4.1'
}
group = 'io.github.fvarrui'
-version = '1.6.5'
+version = '1.7.6'
description = 'Hybrid Maven/Gradle plugin to package Java applications as native Windows, Mac OS X or GNU/Linux executables and create installers for them'
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -78,157 +72,152 @@ targetCompatibility = JavaVersion.VERSION_1_8
compileJava.options.encoding = 'UTF-8'
+// suppress javadoc warnings
+javadoc.options.addStringOption('Xdoclint:none', '-quiet')
+
+java {
+ withSourcesJar()
+ withJavadocJar()
+}
+
+// OSSRH publication
publishing {
- publications {
- mavenJava(MavenPublication) {
- from(components.java)
+ repositories {
+ maven {
+ def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+ def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots"
+ name = "OSSRH"
+ url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
+ credentials {
+ username = project.findProperty("ossrhUsername") ?: ''
+ password = project.findProperty("ossrhPassword") ?: ''
+ }
}
- }
-}
+ }
+ publications {
+ pluginMaven(MavenPublication) {
+ def directory = buildDir.canonicalPath
+ def outputDirectory = compileJava.destinationDirectory.asFile.get().canonicalPath
+ pom.withXml {
+ asNode().appendNode('build')
+ .with {
+ appendNode('directory', directory)
+ appendNode('outputDirectory', outputDirectory)
+ }
+ asNode().appendNode('repositories').appendNode('repository')
+ .with {
+ appendNode('id', 'gradle')
+ appendNode('name', 'Gradle Plugin Portal')
+ appendNode('url', 'https://plugins.gradle.org/m2/')
+ }
+ }
+ pom {
+ name = project.name
+ groupId = project.group
+ artifactId = project.name
+ packaging = 'maven-plugin'
+ }
+ }
+ }
-install.repositories.mavenInstaller.pom.with {
- groupId = project.group
- artifactId = project.name
- version = project.version
- description = project.description
- packaging = 'maven-plugin'
+ afterEvaluate {
+ publications {
+ withType(MavenPublication) {
+ // customize all publications here
+ pom {
+ version = project.version
+ description = project.description
+ url = 'https://github.com/fvarrui/JavaPackager'
+ scm {
+ connection = 'scm:git:git://github.com/fvarrui/JavaPackager.git'
+ developerConnection = 'scm:git:git@github.com:fvarrui/fvarrui.git'
+ url = 'https://github.com/fvarrui/JavaPackager'
+ }
+ licenses {
+ license {
+ name = 'GPL-v3.0'
+ url = 'http://www.gnu.org/licenses/gpl-3.0.txt'
+ distribution = 'repo'
+ }
+ }
+ developers {
+ developer {
+ id = 'fvarrui'
+ name = 'Francisco Vargas Ruiz'
+ url = 'https://github.com/fvarrui'
+ }
+ }
+ }
+ }
+ }
+ }
}
-build.dependsOn ':winrun4j-launcher:build'
-
// runs the plugin description generator
-task generatePluginDescriptor(type: JavaExec, dependsOn: compileJava) {
-
- def pomFile = file("$buildDir/pom.xml")
- def pluginDescriptorFile = new File(project.compileJava.destinationDir, 'META-INF/maven/plugin.xml')
- def directory = buildDir.canonicalPath
- def outputDirectory = compileJava.destinationDir.canonicalPath
-
- // FIXME: this does not seem to be working
- inputs.files project.compileJava.outputs.files
- outputs.file pluginDescriptorFile
-
- classpath = configurations.mavenEmbedder
- main = 'org.apache.maven.cli.MavenCli'
- systemProperties['maven.multiModuleProjectDirectory'] = projectDir
- args = [
- '--errors',
- '--batch-mode',
- '--file', "${buildDir}/pom.xml",
- 'org.apache.maven.plugins:maven-plugin-plugin:3.6.0:descriptor',
- '-Dproject.build.sourceEncoding=' + compileJava.options.encoding
- ]
-
- doFirst {
- install.repositories
- .mavenInstaller
- .pom
- .withXml {
- asNode().appendNode('repositories').appendNode('repository')
- .with {
- appendNode('id', 'gradle')
- appendNode('name', 'Gradle Plugin Portal')
- appendNode('url', 'https://plugins.gradle.org/m2/')
- }
- asNode().appendNode('build')
- .with {
- appendNode('directory', directory)
- appendNode('outputDirectory', outputDirectory)
- }
- }
- .writeTo(pomFile)
-
- assert pomFile.file, "${pomFile.canonicalPath}: was not generated"
- logger.info("POM is generated in ${pomFile.canonicalPath}")
- }
-
- doLast {
- assert pluginDescriptorFile.file, "${pluginDescriptorFile.canonicalPath}: was not generated"
- logger.info("Plugin descriptor is generated in ${pluginDescriptorFile.canonicalPath}")
- }
+tasks.register('generatePluginDescriptor', JavaExec) {
+ dependsOn compileJava
+ def pluginDescriptorFile = new File(project.compileJava.destinationDirectory.asFile.get(), 'META-INF/maven/plugin.xml')
+
+ // FIXME: this does not seem to be working
+ inputs.files project.compileJava.outputs.files
+ outputs.file pluginDescriptorFile
+
+ classpath = configurations.mavenEmbedder
+ mainClass = 'org.apache.maven.cli.MavenCli'
+ systemProperties['maven.multiModuleProjectDirectory'] = projectDir
+ args = [
+ '--errors',
+ '--batch-mode',
+ '--file', generatePomFileForPluginMavenPublication.destination,
+ 'org.apache.maven.plugins:maven-plugin-plugin:3.9.0:descriptor',
+ '-Dproject.build.sourceEncoding=' + compileJava.options.encoding
+ ]
+ doLast {
+ assert pluginDescriptorFile.file, "${pluginDescriptorFile.canonicalPath}: was not generated"
+ logger.info("Plugin descriptor is generated in ${pluginDescriptorFile.canonicalPath}")
+ }
}
+generatePluginDescriptor.dependsOn(generatePomFileForPluginMavenPublication)
-project.jar.dependsOn(generatePluginDescriptor)
+project.classes.dependsOn(generatePluginDescriptor)
+project.validatePlugins.dependsOn(generatePluginDescriptor)
publishToMavenLocal.dependsOn(build)
-
-if (project.hasProperty('release')) {
- apply plugin: 'signing'
- apply plugin: 'maven'
-
- task javadocJar(type: Jar) {
- classifier = 'javadoc'
- from javadoc
- }
-
- task sourcesJar(type: Jar) {
- classifier = 'sources'
- from sourceSets.main.allSource
+// Signature of publication
+signing {
+ setRequired {
+ // signing is only required if the artifacts are to be published
+ gradle.taskGraph.allTasks.any { it instanceof PublishToMavenRepository }
}
-
- artifacts {
- archives javadocJar, sourcesJar
- }
-
- // Signature of artifacts
- signing {
- sign configurations.archives
- }
-
- // OSSRH publication
- uploadArchives {
- repositories {
- mavenDeployer {
- // POM signature
- beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
- // Target repository
- repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
- authentication(userName: ossrhUser, password: ossrhPassword)
- }
- pom.project {
- name project.name
- description project.description
- packaging = 'maven-plugin'
- url 'https://github.com/fvarrui/JavaPackager'
-
- scm {
- connection 'scm:git:git://github.com/fvarrui/JavaPackager.git'
- developerConnection 'scm:git:git@github.com:fvarrui/fvarrui.git'
- url 'https://github.com/fvarrui/JavaPackager'
- }
-
- licenses {
- license {
- name 'GPL-v3.0'
- url 'http://www.gnu.org/licenses/gpl-3.0.txt'
- distribution 'repo'
- }
- }
-
- developers {
- developer {
- id = 'fvarrui'
- name = 'Francisco Vargas Ruiz'
- url = 'https://github.com/fvarrui'
- }
- }
- }
- }
- }
-
- }
-
+ sign publishing.publications.pluginMaven
}
nexusStaging {
- username = project.findProperty('ossrhUser') ?: ''
+ username = project.findProperty('ossrhUsername') ?: ''
password = project.findProperty('ossrhPassword') ?: ''
}
-task updateUniversalJavaApplicationStub(type: Download) {
- group 'Update'
- description 'Downloads universalJavaApplicationStub to src/main/resources/mac and overrides the existing one.'
- src 'https://raw.githubusercontent.com/fvarrui/universalJavaApplicationStub/master/src/universalJavaApplicationStub'
+tasks.register('updateUniversalJavaApplicationStub', Download) {
+ def version = '20230601.235708'
+ group 'Update assets'
+ description 'Downloads compiled and scripted versions of universalJavaApplicationStub to src/main/resources/mac overriding the existing ones.'
+ src([
+ "https://github.com/fvarrui/universalJavaApplicationStub/releases/download/${version}/universalJavaApplicationStub.sh",
+ "https://github.com/fvarrui/universalJavaApplicationStub/releases/download/${version}/universalJavaApplicationStub.x86_64",
+ "https://github.com/fvarrui/universalJavaApplicationStub/releases/download/${version}/universalJavaApplicationStub.arm64",
+ "https://github.com/fvarrui/universalJavaApplicationStub/releases/download/${version}/universalJavaApplicationStub"
+ ])
dest file('src/main/resources/mac')
overwrite true
}
+
+tasks.register('updateWhyJavaLauncher', Download) {
+ def version = '1.1.2'
+ group 'Update assets'
+ description 'Downloads JavaLauncher.exe to src/main/resources/windows and overwrites the existing one.'
+ src([
+ "https://github.com/AstroImageJ/Why/releases/download/${version}/JavaLauncher.exe"
+ ])
+ dest file('src/main/resources/windows')
+ overwrite true
+}
diff --git a/docs/gradle/plugin-configuration-samples.md b/docs/gradle/plugin-configuration-samples.md
index 65aa30c8..60411c08 100644
--- a/docs/gradle/plugin-configuration-samples.md
+++ b/docs/gradle/plugin-configuration-samples.md
@@ -120,7 +120,7 @@ task packageMyApp(dependsOn: [ 'packageMyAppForLinux', 'packageMyAppForMac', 'pa
E.g. on Windows, running `packageMyApp` task will generate next artifacts:
* `${name}_${version}-linux.tar.gz` with the GNU/Linux application including a customized JRE.
-* `${name}_${version}-mac.tar.gz` with the Mac OS X application including a customized JRE.
+* `${name}_${version}-mac.tar.gz` with the MacOS application including a customized JRE.
* `${name}_${version}-windows.zip` with the Windows application including a customized JRE.
-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 Mac OS X, you have to specify a JDK for Windows.
+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.
diff --git a/docs/linux-specific-properties.md b/docs/linux-specific-properties.md
index 6b7f3845..316f8a5e 100644
--- a/docs/linux-specific-properties.md
+++ b/docs/linux-specific-properties.md
@@ -2,18 +2,23 @@
```xml
- path/to/icon.png
- true|false
- true|false
+ path/to/icon.png
+ true|false
+ true|false
+ true|false
true|false
+
+ Utility
+ ...
+
```
-
-
-| Property | Mandatory | Default value | Description |
-| ------------- | --------- | ------------- | ----------------------------------------------- |
-| `pngFile` | :x: | `null` | Icon file. |
-| `generateDeb` | :x: | `true` | DEB package will be generated. |
-| `generateRpm` | :x: | `true` | RPM package will be generated. |
-| `wrapJar` | :x: | `true` | Wraps JAR file inside the executable if `true`. |
+| Property | Mandatory | Default value | Description |
+| ------------------ | --------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| `pngFile` | :x: | `null` | Icon file. |
+| `generateAppImage` | :x: | `true` | [AppImage](https://appimage.org/) package will be generated. |
+| `generateDeb` | :x: | `true` | DEB package will be generated. |
+| `generateRpm` | :x: | `true` | RPM package will be generated. |
+| `wrapJar` | :x: | `true` | Wraps JAR file inside the executable if `true`. |
+| `categories` | :x: | `[ "Utility"]` | [Main categories](https://specifications.freedesktop.org/menu-spec/latest/apa.html) in the application's desktop entry file. |
diff --git a/docs/macosx-specific-properties.md b/docs/macosx-specific-properties.md
index c0ac8d99..d104c2e4 100644
--- a/docs/macosx-specific-properties.md
+++ b/docs/macosx-specific-properties.md
@@ -1,4 +1,4 @@
-# Mac OS X specific properties
+# MacOS specific properties
```xml
@@ -9,11 +9,15 @@
true|false
true|false
true|false
+ SCRIPT|UNIVERSAL|X86_64|ARM64
singning identity
path/to/entitlements.plist
true|false
+ true|false
+ true|false
+ xcrun_notarytool_profile_name
path/to/png
@@ -47,16 +51,27 @@
```
-| Property | Mandatory | Default value | Description |
-| -------------- | --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| `icnsFile` | :x: | `null` | Icon file. |
-| `generateDmg` | :x: | `true` | Enables DMG disk image file generation. |
-| `generatePkg` | :x: | `true` | Enables installation package generation. |
-| `relocateJar` | :x: | `true` | If `true`, Jar files are located in `Contents/Resources/Java` folder, otherwise they are located in `Contents/Resources` folder. |
-| `appId` | :x: | `${mainClass}` | App unique identifier. |
-| `developerId` | :x: | `null` | Signing identity. |
-| `entitlements` | :x: | `null` | Path to [entitlements](https://developer.apple.com/documentation/bundleresources/entitlements) file. |
-| `codesignApp` | :x: | `true` | If it is set to `false`, generated app will not be codesigned. |
+| Property | Mandatory | Default value | Description |
+| ------------- | --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| `icnsFile` | :x: | | ICNS format icon file.\* |
+| `generateDmg` | :x: | `true` | Enables DMG disk image file generation. |
+| `generatePkg` | :x: | `true` | Enables installation package generation. |
+| `relocateJar` | :x: | `true` | If `true`, Jar files are located in `Contents/Resources/Java` folder, otherwise they are located in `Contents/Resources` folder. |
+| `appId` | :x: | `${mainClass}` | App unique identifier. |
+
+> \* You can use [MacICNS](https://github.com/EasyG0ing1/MacIcns) to convert your PNG icon file to ICNS.
+
+## Signing properties
+
+| Property | Mandatory | Default value | Description |
+|--------------------| --------- |---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
+| `developerId` | :x: | | Signing identity. |
+| `entitlements` | :x: | | Path to [entitlements](https://developer.apple.com/documentation/bundleresources/entitlements) file. |
+| `codesignApp` | :x: | `true` | If it is set to `false`, generated app will not be codesigned. |
+| `hardenedCodesign` | :x: | `true` | If it is set to `true`, enable [hardened runtime](https://developer.apple.com/documentation/security/hardened_runtime) if MacOS version >= 10.13.6. |
+| `notarizeApp` | :x: | `false` | If it is set to `true`, generated app will be submitted to apple for notarization and the ticket will be stapled. |
+| `keyChainProfile` | :x: | | Profile name originally provided to `xcrun notarytool store-credentials`. Must be set if `notarizeApp` is `true`.
+| `macStartup` | :x: | `SCRIPT` | App startup type, using a `SCRIPT` or a binary (compiled version of the script:Â `UNIVERSAL`, `X86_64`Â or `ARM64`). |
## DMG generation properties
diff --git a/docs/maven/plugin-configuration-samples.md b/docs/maven/plugin-configuration-samples.md
index fc4a1cfd..db759041 100644
--- a/docs/maven/plugin-configuration-samples.md
+++ b/docs/maven/plugin-configuration-samples.md
@@ -221,7 +221,7 @@ E.g. on Windows, last configuration will generate next artifacts:
E.g. on Windows, last configuration will generate next artifacts:
* `${name}_${version}-linux.tar.gz` with the GNU/Linux application including a customized JRE.
-* `${name}_${version}-mac.tar.gz` with the Mac OS X application including a customized JRE.
+* `${name}_${version}-mac.tar.gz` with the MacOS application including a customized JRE.
* `${name}_${version}-windows.zip` with the Windows application including a customized JRE.
-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 Mac OS X, you have to specify a JDK for Windows.
\ No newline at end of file
+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.
diff --git a/docs/windows-specific-properties.md b/docs/windows-specific-properties.md
index 42d32073..aefe27aa 100644
--- a/docs/windows-specific-properties.md
+++ b/docs/windows-specific-properties.md
@@ -10,7 +10,7 @@
true|false
- gui
+ gui|console
true|false
${organizationName}
1.0.0.0
@@ -21,10 +21,11 @@
${organizationName}
${name}
${name}
+ ${name}
${name}.exe
- launch4j|winrun4j
+ launch4j|winrun4j|why
relative/path/to/jvm.dll
@@ -55,10 +56,10 @@
- root:path/to/my/key
- name
- type
- data
+ root:path/to/my/key
+ name
+ type
+ data
[...]
@@ -76,41 +77,40 @@
## Exe creation properties
-| Property | Mandatory | Default value | Description |
-| ------------------- | --------- | --------------------- | ---------------------------------------------------------------------- |
-| `headerType` | :x: | `"gui"` | EXE header type: `console` or `gui`. |
-| `wrapJar` | :x: | `true` | Wrap JAR file in native EXE. |
-| `companyName` | :x: | `${organizationName}` | EXE company name. |
-| `fileVersion` | :x: | `"1.0.0.0"` | EXE file version. |
-| `txtFileVersion` | :x: | `${version}` | EXE txt file version. |
-| `productVersion` | :x: | `"1.0.0.0"` | EXE product version. |
-| `txtProductVersion` | :x: | `${version}` | EXE txt product version. |
-| `fileDescription` | :x: | `${description}` | EXE file description. |
-| `copyright` | :x: | `${organizationName}` | EXE copyright. |
-| `productName` | :x: | `${name}` | EXE product name. |
-| `internalName` | :x: | `${name}` | EXE internal name. |
-| `originalFilename` | :x: | `${name}.exe` | EXE original filename. |
-| `trademark` | :x: | | EXE trademark. |
-| `language` | :x: | | EXE language. |
-| `exeCreationTool` | :x: | `launch4j` | EXE creation tool: `launch    4j` or `winrun4j`. |
-| `vmLocation` | :x: | | `jvm.dll` path relative to the bundled JRE. **Only for WinRun4J**. \*1 |
+| Property | Mandatory | Default value | Description |
+| ------------------- | --------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `headerType` | :x: | `"gui"` | EXE header type: `console` or `gui`. |
+| `wrapJar` | :x: | `true` | Wrap JAR file in native EXE. |
+| `companyName` | :x: | `${organizationName}` | EXE company name. |
+| `fileVersion` | :x: | `"1.0.0.0"` | EXE file version. |
+| `txtFileVersion` | :x: | `${version}` | EXE txt file version. |
+| `productVersion` | :x: | `"1.0.0.0"` | EXE product version. |
+| `txtProductVersion` | :x: | `${version}` | EXE txt product version. |
+| `fileDescription` | :x: | `${description}` | EXE file description. |
+| `copyright` | :x: | `${organizationName}` | EXE copyright. |
+| `productName` | :x: | `${name}` | EXE product name. |
+| `internalName` | :x: | `${name}` | EXE internal name. |
+| `originalFilename` | :x: | `${name}.exe` | EXE original filename. |
+| `trademark` | :x: | | EXE trademark. |
+| `language` | :x: | | EXE language. |
+| `exeCreationTool` | :x: | `launch4j` | EXE creation tool: [`launch4j`](http://launch4j.sourceforge.net/), [`winrun4j`](http://winrun4j.sourceforge.net/)Â or [`why`](https://github.com/AstroImageJ/Why). |
+| `vmLocation` | :x: | | `jvm.dll` path relative to the bundled JRE. **Only for WinRun4J**. \*1 |
> \*1: If a JRE is bundled and `vmLocation` is not specified, it uses `bin\client\jvm.dll` or `bin\server\jvm.dll` as VM location by default.
-
-
## Setup generation properties
| Property | Mandatory | Default value | Description |
| ------------------------- | --------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| `setupMode` | :x: | `installForAllUsers` | Setup installation mode: require administrative privileges or not. [*](#setupmode) |
| `setupLanguages` | :x: | `compiler:Default.islcompiler:Languages\Spanish.isl` | Map with setup languages. |
+| `shortcutName` | :x: | `${name}` | Sets the name of the user optional shortcut created on the desktop |
| `disableDirPage` | :x: | `true` | If this is set to `true`, Setup will not show the **Select Destination Location** wizard page. |
| `disableProgramGroupPage` | :x: | `true` | If this is set to `true`, Setup will not show the **Select Start Menu Folder** wizard page. |
| `disableFinishedPage` | :x: | `true` | If this is set to `true`, Setup will not show the **Setup Completed** wizard page. |
| `disableRunAfterInstall` | :x: | `true` | If this is set to `true`, Setup will not **launch the application** after install. |
| `disableWelcomePage` | :x: | `true` | If this is set to `true`, Setup will not show **Welcome Page**. |
-| `createDesktopIconTask` | :x: | `true` | If this is set to `true`, Setup will not ask for **desktop icon creation**. |
+| `createDesktopIconTask` | :x: | `true` | If this is set to `true`, Setup will ask for **desktop icon creation**. |
| `removeOldLibs` | :x: | `false` | If there's a previous installation, removes old `libs` folder before install. |
### SetupMode
diff --git a/docs/windows-tools-guide.md b/docs/windows-tools-guide.md
index 2a4fa382..e41b8391 100644
--- a/docs/windows-tools-guide.md
+++ b/docs/windows-tools-guide.md
@@ -1,6 +1,8 @@
# Windows tools installation guide
-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 a MSI file.
+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.
+
+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.
## Using Chocolatey
You can install both tools in a simple way using [Chocolatey](https://chocolatey.org/) package manager:
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e64ee76b..e1bef7e8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
\ No newline at end of file
+zipStorePath=wrapper/dists
diff --git a/settings.gradle b/settings.gradle
index 03800abd..b4ae1d88 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1 @@
-rootProject.name = 'javapackager'
-include 'winrun4j-launcher'
\ No newline at end of file
+rootProject.name = 'javapackager'
\ No newline at end of file
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/AbstractPackageTask.java b/src/main/java/io/github/fvarrui/javapackager/gradle/AbstractPackageTask.java
index 5cf1bc8a..e8f94a8e 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/AbstractPackageTask.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/AbstractPackageTask.java
@@ -28,7 +28,7 @@ public List getOutputFiles() {
public AbstractPackageTask() {
super();
setGroup(PackagePlugin.GROUP_NAME);
- setDescription("Packages the application as a native Windows, Mac OS X or GNU/Linux executable and creates an installer");
+ setDescription("Packages the application as a native Windows, MacOS or GNU/Linux executable and creates an installer");
getOutputs().upToDateWhen(o -> false);
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/CopyDependencies.java b/src/main/java/io/github/fvarrui/javapackager/gradle/CopyDependencies.java
index a4f0e250..f6881ad0 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/CopyDependencies.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/CopyDependencies.java
@@ -35,7 +35,8 @@ protected File doApply(Packager packager) {
if (copyLibsTask == null) {
copyLibsTask = project.getTasks().create("copyLibs", Copy.class);
}
- copyLibsTask.from(project.getConfigurations().getByName("default"));
+ copyLibsTask.setDuplicatesStrategy(Context.getGradleContext().getDuplicatesStrategy());
+ copyLibsTask.from(project.getConfigurations().getByName("runtimeClasspath"));
copyLibsTask.into(project.file(libsFolder));
copyLibsTask.getActions().forEach(action -> action.execute(copyLibsTask));
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateTarball.java b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateTarball.java
index f2ce76f7..917fac8d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateTarball.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateTarball.java
@@ -36,8 +36,11 @@ protected File doApply(Packager packager) throws Exception {
File appFolder = packager.getAppFolder();
File executable = packager.getExecutable();
String jreDirectoryName = packager.getJreDirectoryName();
-
- File tarFile = new File(outputDirectory, name + "-" + version + "-" + platform + ".tar.gz");
+
+ // tgz file name
+ String finalName = packager.getTarballName() != null ? packager.getTarballName() : name + "-" + version + "-" + platform;
+ String format = ".tar.gz";
+ File tarFile = new File(outputDirectory, finalName + format);
Tar tarTask = createTarTask();
tarTask.setProperty("archiveFileName", tarFile.getName());
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateWindowsExeLaunch4j.java b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateWindowsExeLaunch4j.java
index 5c59aa32..0af1f1a5 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateWindowsExeLaunch4j.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateWindowsExeLaunch4j.java
@@ -4,10 +4,12 @@
import java.util.HashSet;
import java.util.List;
+import net.jsign.WindowsSigner;
import org.apache.commons.lang3.StringUtils;
import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask;
import io.github.fvarrui.javapackager.model.WindowsConfig;
+import io.github.fvarrui.javapackager.model.WindowsExeCreationTool;
import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe;
import io.github.fvarrui.javapackager.packagers.Context;
import io.github.fvarrui.javapackager.packagers.WindowsPackager;
@@ -19,7 +21,7 @@
public class CreateWindowsExeLaunch4j extends AbstractCreateWindowsExe {
public CreateWindowsExeLaunch4j() {
- super("launch4j");
+ super(WindowsExeCreationTool.launch4j);
}
@Override
@@ -34,42 +36,49 @@ protected File doApply(WindowsPackager packager) throws Exception {
String jreDirectoryName = packager.getJreDirectoryName();
String jreMinVersion = packager.getJreMinVersion();
File jarFile = packager.getJarFile();
+ File appFolder = packager.getAppFolder();
createAssets(packager); // creates a folder only for launch4j assets
- String jarPath = winConfig.isWrapJar() ? getGenericJar().getAbsolutePath() : jarFile.getName();
+ // copies JAR to app folder
+ String jarPath;
+ if (winConfig.isWrapJar()) {
+ jarPath = getGenericJar().getAbsolutePath();
+ } else {
+ FileUtils.copyFileToFolder(jarFile, appFolder);
+ jarPath = jarFile.getName();
+ }
Launch4jLibraryTask l4jTask = Context.getGradleContext().getLibraryTask();
+ l4jTask.getDuplicatesStrategy().set(Context.getGradleContext().getDuplicatesStrategy());
l4jTask.getOutputs().upToDateWhen(task -> false);
- l4jTask.setHeaderType(winConfig.getHeaderType().toString());
- l4jTask.setJar(jarPath);
- l4jTask.setDontWrapJar(!winConfig.isWrapJar());
- l4jTask.setOutfile(getGenericExe().getName());
- l4jTask.setIcon(getGenericIcon().getAbsolutePath());
- l4jTask.setManifest(getGenericManifest().getAbsolutePath());
- l4jTask.setMainClassName(mainClass);
- l4jTask.setClasspath(new HashSet<>(packager.getClasspaths()));
- l4jTask.setChdir(useResourcesAsWorkingDir ? "." : "");
+ l4jTask.getHeaderType().set(winConfig.getHeaderType().toString());
+ l4jTask.getJarFiles().set(Context.getGradleContext().getProject().files(jarPath));
+ l4jTask.getDontWrapJar().set(!winConfig.isWrapJar());
+ l4jTask.getOutfile().set(getGenericExe().getName());
+ l4jTask.getIcon().set(getGenericIcon().getAbsolutePath());
+ l4jTask.getManifest().set(getGenericManifest().getAbsolutePath());
+ l4jTask.getMainClassName().set(mainClass);
+ l4jTask.getClasspath().set(new HashSet<>(packager.getClasspaths()));
+ l4jTask.getChdir().set(useResourcesAsWorkingDir ? "." : "");
if (bundleJre) {
- l4jTask.setBundledJrePath(jreDirectoryName);
+ l4jTask.getBundledJrePath().set(jreDirectoryName);
}
if (!StringUtils.isBlank(jreMinVersion)) {
- l4jTask.setJreMinVersion(jreMinVersion);
+ l4jTask.getJreMinVersion().set(jreMinVersion);
}
l4jTask.getJvmOptions().addAll(vmArgs);
- l4jTask.setVersion(winConfig.getProductVersion());
- l4jTask.setTextVersion(winConfig.getTxtProductVersion());
- l4jTask.setCopyright(winConfig.getCopyright());
- l4jTask.setCompanyName(winConfig.getCompanyName());
- l4jTask.setFileDescription(winConfig.getFileDescription());
- l4jTask.setProductName(winConfig.getProductName());
- l4jTask.setInternalName(winConfig.getInternalName());
- l4jTask.setTrademarks(winConfig.getTrademarks());
- l4jTask.setLanguage(winConfig.getLanguage());
+ l4jTask.getVersion().set(winConfig.getProductVersion());
+ l4jTask.getTextVersion().set(winConfig.getTxtProductVersion());
+ l4jTask.getCopyright().set(winConfig.getCopyright());
+ l4jTask.getCompanyName().set(winConfig.getCompanyName());
+ l4jTask.getFileDescription().set(winConfig.getFileDescription());
+ l4jTask.getProductName().set(winConfig.getProductName());
+ l4jTask.getInternalName().set(winConfig.getInternalName());
+ l4jTask.getTrademarks().set(winConfig.getTrademarks());
+ l4jTask.getLanguage().set(winConfig.getLanguage());
l4jTask.getActions().forEach(action -> action.execute(l4jTask));
- sign(getGenericExe(), packager);
-
FileUtils.copyFileToFile(getGenericExe(), executable);
return createBootstrapScript(packager);
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateZipball.java b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateZipball.java
index 401c3025..cdd85c46 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/CreateZipball.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/CreateZipball.java
@@ -35,8 +35,9 @@ protected File doApply(Packager packager) throws Exception {
File appFolder = packager.getAppFolder();
File executable = packager.getExecutable();
String jreDirectoryName = packager.getJreDirectoryName();
-
- File zipFile = new File(outputDirectory, name + "-" + version + "-" + platform + ".zip");
+
+ String zipFileName = packager.getZipballName() != null ? packager.getZipballName() : name + "-" + version + "-" + platform + ".zip";
+ File zipFile = new File(outputDirectory, zipFileName);
Zip zipTask = createZipTask();
zipTask.setProperty("archiveFileName", zipFile.getName());
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/DefaultPackageTask.java b/src/main/java/io/github/fvarrui/javapackager/gradle/DefaultPackageTask.java
deleted file mode 100644
index 976eed94..00000000
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/DefaultPackageTask.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package io.github.fvarrui.javapackager.gradle;
-
-import static io.github.fvarrui.javapackager.utils.ObjectUtils.defaultIfNull;
-
-import io.github.fvarrui.javapackager.packagers.Packager;
-import io.github.fvarrui.javapackager.packagers.PackagerFactory;
-
-/**
- * Default packaging task for Gradle
- */
-public class DefaultPackageTask extends AbstractPackageTask {
-
- @Override
- protected Packager createPackager() throws Exception {
-
- PackagePluginExtension extension = getProject().getExtensions().findByType(PackagePluginExtension.class);
-
- return
- (Packager) PackagerFactory
- .createPackager(extension.getPlatform())
- .additionalModules(extension.getAdditionalModules())
- .additionalModulePaths(extension.getAdditionalModulePaths())
- .additionalResources(extension.getAdditionalResources())
- .administratorRequired(extension.getAdministratorRequired())
- .assetsDir(extension.getAssetsDir())
- .bundleJre(extension.getBundleJre())
- .copyDependencies(extension.getCopyDependencies())
- .createTarball(extension.getCreateTarball())
- .createZipball(extension.getCreateZipball())
- .customizedJre(extension.getCustomizedJre())
- .description(extension.getDescription())
- .displayName(extension.getDisplayName())
- .envPath(extension.getEnvPath())
- .extra(extension.getExtra())
- .fileAssociations(extension.getFileAssociations())
- .generateInstaller(extension.getGenerateInstaller())
- .jdkPath(extension.getJdkPath())
- .jreDirectoryName(extension.getJreDirectoryName())
- .jreMinVersion(extension.getJreMinVersion())
- .jrePath(extension.getJrePath())
- .licenseFile(extension.getLicenseFile())
- .linuxConfig(extension.getLinuxConfig())
- .macConfig(extension.getMacConfig())
- .mainClass(extension.getMainClass())
- .manifest(extension.getManifest())
- .modules(extension.getModules())
- .name(extension.getName())
- .organizationEmail(extension.getOrganizationEmail())
- .organizationName(extension.getOrganizationName())
- .organizationUrl(extension.getOrganizationUrl())
- .outputDirectory(extension.getOutputDirectory())
- .packagingJdk(extension.getPackagingJdk())
- .runnableJar(extension.getRunnableJar())
- .useResourcesAsWorkingDir(extension.isUseResourcesAsWorkingDir())
- .url(extension.getUrl())
- .version(defaultIfNull(extension.getVersion(), getProject().getVersion().toString()))
- .vmArgs(extension.getVmArgs())
- .winConfig(extension.getWinConfig());
-
- }
-
-}
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/GenerateRpm.java b/src/main/java/io/github/fvarrui/javapackager/gradle/GenerateRpm.java
deleted file mode 100644
index 20a17ff7..00000000
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/GenerateRpm.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package io.github.fvarrui.javapackager.gradle;
-
-import java.io.File;
-
-import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.packagers.ArtifactGenerator;
-import io.github.fvarrui.javapackager.packagers.LinuxPackager;
-import io.github.fvarrui.javapackager.utils.Logger;
-
-/**
- * Creates a RPM package file including all app folder's content only for
- * GNU/Linux so app could be easily distributed on Gradle context
- */
-public class GenerateRpm extends ArtifactGenerator {
-
- public GenerateRpm() {
- super("RPM package");
- }
-
- @Override
- public boolean skip(LinuxPackager packager) {
- return !packager.getLinuxConfig().isGenerateRpm() || !Platform.linux.isCurrentPlatform();
- }
-
- @Override
- protected File doApply(LinuxPackager packager) throws Exception {
-
- Logger.warn("Sorry! " + getArtifactName() + " generation is not yet available");
-
- return null;
- }
-
-}
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/GradleContext.java b/src/main/java/io/github/fvarrui/javapackager/gradle/GradleContext.java
index 024e3e7f..b2e1f206 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/GradleContext.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/GradleContext.java
@@ -2,7 +2,9 @@
import java.io.File;
+import io.github.fvarrui.javapackager.packagers.*;
import org.gradle.api.Project;
+import org.gradle.api.file.DuplicatesStrategy;
import org.gradle.api.internal.provider.Providers;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPluginExtension;
@@ -12,11 +14,6 @@
import org.gradle.jvm.toolchain.JavaToolchainSpec;
import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask;
-import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe;
-import io.github.fvarrui.javapackager.packagers.Context;
-import io.github.fvarrui.javapackager.packagers.CreateWindowsExeWinRun4j;
-import io.github.fvarrui.javapackager.packagers.Packager;
-import io.github.fvarrui.javapackager.packagers.WindowsPackager;
/**
* Gradle context
@@ -24,8 +21,8 @@
public class GradleContext extends Context {
private Project project;
-
private Launch4jLibraryTask libraryTask;
+ private DuplicatesStrategy duplicatesStrategy;
public GradleContext(Project project) {
super();
@@ -84,6 +81,14 @@ public void setLibraryTask(Launch4jLibraryTask libraryTask) {
this.libraryTask = libraryTask;
}
+ public DuplicatesStrategy getDuplicatesStrategy() {
+ return duplicatesStrategy;
+ }
+
+ public void setDuplicatesStrategy(DuplicatesStrategy duplicatesStrategy) {
+ this.duplicatesStrategy = duplicatesStrategy;
+ }
+
/**
* Returns project's default toolchain
*
@@ -118,6 +123,7 @@ public File createWindowsExe(WindowsPackager packager) throws Exception {
switch (packager.getWinConfig().getExeCreationTool()) {
case launch4j: createWindowsExe = new CreateWindowsExeLaunch4j(); break;
case winrun4j: createWindowsExe = new CreateWindowsExeWinRun4j(); break;
+ case why: createWindowsExe = new CreateWindowsExeWhy(); break;
default: return null;
}
if (!createWindowsExe.skip(packager)) {
@@ -125,7 +131,5 @@ public File createWindowsExe(WindowsPackager packager) throws Exception {
}
return null;
}
-
-
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePlugin.java b/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePlugin.java
index acc47b3d..b817168b 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePlugin.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePlugin.java
@@ -1,11 +1,12 @@
package io.github.fvarrui.javapackager.gradle;
-import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask;
-import io.github.fvarrui.javapackager.packagers.Context;
+import java.util.UUID;
+
import org.gradle.api.Plugin;
import org.gradle.api.Project;
-import java.util.UUID;
+import edu.sc.seis.launch4j.tasks.Launch4jLibraryTask;
+import io.github.fvarrui.javapackager.packagers.Context;
/**
* JavaPackager Gradle plugin
@@ -18,14 +19,13 @@ public class PackagePlugin implements Plugin {
@Override
public void apply(Project project) {
-
Context.setContext(new GradleContext(project));
project.getPluginManager().apply("java");
project.getPluginManager().apply("edu.sc.seis.launch4j");
project.getExtensions().create(SETTINGS_EXT_NAME, PackagePluginExtension.class, project);
- project.getTasks().create(PACKAGE_TASK_NAME, DefaultPackageTask.class).dependsOn("build");
+ project.getTasks().create(PACKAGE_TASK_NAME, PackageTask.class).dependsOn("build");
Context.getGradleContext().setLibraryTask(project.getTasks().create("launch4j_" + UUID.randomUUID(), Launch4jLibraryTask.class));
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePluginExtension.java b/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePluginExtension.java
index 966406a7..da793647 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePluginExtension.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/PackagePluginExtension.java
@@ -5,8 +5,10 @@
import java.util.HashMap;
import org.gradle.api.Project;
+import org.gradle.api.file.DuplicatesStrategy;
import groovy.lang.Closure;
+import io.github.fvarrui.javapackager.model.Arch;
import io.github.fvarrui.javapackager.model.LinuxConfig;
import io.github.fvarrui.javapackager.model.MacConfig;
import io.github.fvarrui.javapackager.model.Manifest;
@@ -21,6 +23,7 @@
public class PackagePluginExtension extends PackagerSettings {
private Project project;
+ private DuplicatesStrategy duplicatesStrategy;
public PackagePluginExtension(Project project) {
super();
@@ -51,6 +54,9 @@ public PackagePluginExtension(Project project) {
this.winConfig = new WindowsConfig();
this.outputDirectory = project.getBuildDir();
this.scripts = new Scripts();
+ this.forceInstaller = false;
+ this.arch = Arch.getDefault();
+ this.duplicatesStrategy = DuplicatesStrategy.WARN;
}
public LinuxConfig linuxConfig(Closure closure) {
@@ -82,5 +88,13 @@ public Scripts scripts(Closure closure) {
project.configure(scripts, closure);
return scripts;
}
+
+ public void setDuplicatesStrategy(DuplicatesStrategy duplicatesStrategy) {
+ this.duplicatesStrategy = duplicatesStrategy;
+ }
+
+ public DuplicatesStrategy getDuplicatesStrategy() {
+ return duplicatesStrategy;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/io/github/fvarrui/javapackager/gradle/PackageTask.java b/src/main/java/io/github/fvarrui/javapackager/gradle/PackageTask.java
index e94d2abf..ce6a321a 100644
--- a/src/main/java/io/github/fvarrui/javapackager/gradle/PackageTask.java
+++ b/src/main/java/io/github/fvarrui/javapackager/gradle/PackageTask.java
@@ -7,6 +7,7 @@
import java.util.List;
import java.util.Map;
+import org.gradle.api.file.DuplicatesStrategy;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.InputFile;
@@ -14,12 +15,14 @@
import org.gradle.api.tasks.OutputDirectory;
import groovy.lang.Closure;
+import io.github.fvarrui.javapackager.model.Arch;
import io.github.fvarrui.javapackager.model.FileAssociation;
import io.github.fvarrui.javapackager.model.LinuxConfig;
import io.github.fvarrui.javapackager.model.MacConfig;
import io.github.fvarrui.javapackager.model.Manifest;
import io.github.fvarrui.javapackager.model.Platform;
import io.github.fvarrui.javapackager.model.Scripts;
+import io.github.fvarrui.javapackager.model.Template;
import io.github.fvarrui.javapackager.model.WindowsConfig;
import io.github.fvarrui.javapackager.packagers.Context;
import io.github.fvarrui.javapackager.packagers.Packager;
@@ -226,6 +229,18 @@ public void setGenerateInstaller(Boolean generateInstaller) {
this.generateInstaller = generateInstaller;
}
+ @Input
+ @Optional
+ private Boolean forceInstaller;
+
+ public Boolean isForceInstaller() {
+ return forceInstaller;
+ }
+
+ public void setForceInstaller(Boolean forceInstaller) {
+ this.forceInstaller = forceInstaller;
+ }
+
@InputDirectory
@Optional
private File jdkPath;
@@ -547,6 +562,42 @@ public Scripts scripts(Closure closure) {
getProject().configure(scripts, closure);
return scripts;
}
+
+ @Input
+ @Optional
+ private Arch arch;
+
+ public Arch getArch() {
+ return arch;
+ }
+
+ public void setArch(Arch arch) {
+ this.arch = arch;
+ }
+
+ @Input
+ @Optional
+ private List templates;
+
+ public List getTemplates() {
+ return templates;
+ }
+
+ public void setTemplates(List templates) {
+ this.templates = templates;
+ }
+
+ @Input
+ @Optional
+ private DuplicatesStrategy duplicatesStrategy;
+
+ public DuplicatesStrategy getDuplicatesStrategy() {
+ return duplicatesStrategy;
+ }
+
+ public void setDuplicatesStrategy(DuplicatesStrategy duplicatesStrategy) {
+ this.duplicatesStrategy = duplicatesStrategy;
+ }
// ===============
// create packager
@@ -558,6 +609,8 @@ protected Packager createPackager() throws Exception {
PackagePluginExtension extension = getProject().getExtensions().findByType(PackagePluginExtension.class);
+ Context.getGradleContext().setDuplicatesStrategy(defaultIfNull(duplicatesStrategy, extension.getDuplicatesStrategy()));
+
return
(Packager) PackagerFactory
.createPackager(defaultIfNull(platform, extension.getPlatform()))
@@ -565,6 +618,7 @@ protected Packager createPackager() throws Exception {
.additionalModulePaths(defaultIfNull(additionalModulePaths, extension.getAdditionalModulePaths()))
.additionalResources(defaultIfNull(additionalResources, extension.getAdditionalResources()))
.administratorRequired(defaultIfNull(administratorRequired, extension.getAdministratorRequired()))
+ .arch(defaultIfNull(arch, extension.getArch()))
.assetsDir(defaultIfNull(assetsDir, extension.getAssetsDir()))
.bundleJre(defaultIfNull(bundleJre, extension.getBundleJre()))
.classpath(defaultIfNull(classpath, extension.getClasspath()))
@@ -577,6 +631,7 @@ protected Packager createPackager() throws Exception {
.envPath(defaultIfNull(envPath, extension.getEnvPath()))
.extra(defaultIfNull(extra, extension.getExtra()))
.fileAssociations(defaultIfNull(fileAssociations, extension.getFileAssociations()))
+ .forceInstaller(defaultIfNull(forceInstaller, extension.isForceInstaller()))
.generateInstaller(defaultIfNull(generateInstaller, extension.getGenerateInstaller()))
.jdkPath(defaultIfNull(jdkPath, extension.getJdkPath()))
.jreDirectoryName(defaultIfBlank(jreDirectoryName, extension.getJreDirectoryName()))
@@ -596,6 +651,7 @@ protected Packager createPackager() throws Exception {
.packagingJdk(defaultIfNull(packagingJdk, extension.getPackagingJdk(), Context.getGradleContext().getDefaultToolchain()))
.runnableJar(defaultIfNull(runnableJar, extension.getRunnableJar()))
.scripts(defaultIfNull(scripts, extension.getScripts()))
+ .templates(defaultIfNull(templates, extension.getTemplates()))
.useResourcesAsWorkingDir(defaultIfNull(useResourcesAsWorkingDir, extension.isUseResourcesAsWorkingDir()))
.url(defaultIfNull(url, extension.getUrl()))
.version(defaultIfNull(version, extension.getVersion(), getProject().getVersion().toString()))
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/CreateRunnableJar.java b/src/main/java/io/github/fvarrui/javapackager/maven/CreateRunnableJar.java
index 54582a45..602a11fe 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/CreateRunnableJar.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/CreateRunnableJar.java
@@ -38,21 +38,18 @@ public CreateRunnableJar() {
protected File doApply(Packager packager) {
String classifier = "runnable";
- String name = packager.getName();
- String version = packager.getVersion();
String mainClass = packager.getMainClass();
File outputDirectory = packager.getOutputDirectory();
ExecutionEnvironment env = Context.getMavenContext().getEnv();
Manifest manifest = packager.getManifest();
-
- File jarFile = new File(outputDirectory, name + "-" + version + "-" + classifier + ".jar");
List archive = new ArrayList<>();
archive.add(
element("manifest",
element("addClasspath", "true"),
element("classpathPrefix", "libs/"),
- element("mainClass", mainClass)
+ element("mainClass", mainClass),
+ element("useUniqueVersions", "false")
)
);
if (manifest != null) {
@@ -79,17 +76,17 @@ protected File doApply(Packager packager) {
plugin(
groupId("org.apache.maven.plugins"),
artifactId("maven-jar-plugin"),
- version("3.1.1")
+ version("3.3.0")
),
goal("jar"),
configuration(
element("classifier", classifier),
element("archive", archive.toArray(new Element[archive.size()])),
- element("outputDirectory", jarFile.getParentFile().getAbsolutePath()),
- element("finalName", name + "-" + version)
+ element("outputDirectory", outputDirectory.getAbsolutePath())
),
- env);
-
+ env
+ );
+
} catch (MojoExecutionException e) {
Logger.error("Runnable jar creation failed! " + e.getMessage());
@@ -97,7 +94,12 @@ protected File doApply(Packager packager) {
}
- return jarFile;
+ // gets build.finalName value
+ String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName();
+
+ // creates file pointing to generated jar file
+ return new File(outputDirectory, finalName + "-" + classifier + ".jar");
+
}
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/CreateTarball.java b/src/main/java/io/github/fvarrui/javapackager/maven/CreateTarball.java
index 73a65ffd..1e59a4e4 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/CreateTarball.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/CreateTarball.java
@@ -35,8 +35,6 @@ public boolean skip(Packager packager) {
protected File doApply(Packager packager) {
File assetsFolder = packager.getAssetsFolder();
- String name = packager.getName();
- String version = packager.getVersion();
Platform platform = packager.getPlatform();
File outputDirectory = packager.getOutputDirectory();
@@ -46,8 +44,7 @@ protected File doApply(Packager packager) {
File assemblyFile = new File(assetsFolder, "assembly-tarball-" + platform + ".xml");
VelocityUtils.render(platform + "/assembly.xml.vtl", assemblyFile, packager);
- // tgz file name
- String finalName = name + "-" + version + "-" + platform;
+ // output file format
String format = "tar.gz";
// invokes plugin to assemble tarball
@@ -62,13 +59,23 @@ protected File doApply(Packager packager) {
element("outputDirectory", outputDirectory.getAbsolutePath()),
element("formats", element("format", format)),
element("descriptors", element("descriptor", assemblyFile.getAbsolutePath())),
- element("finalName", finalName),
element("appendAssemblyId", "false")
),
Context.getMavenContext().getEnv()
);
- return new File(outputDirectory, finalName + "." + format);
+ // get generated filename
+ String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName();
+ File finalFile = new File(outputDirectory, finalName + "." + format);
+
+ // get desired file name
+ String tarName = packager.getTarballName() != null ? packager.getTarballName() : finalName + "-" + platform;
+ File tarFile = new File(outputDirectory, tarName + "." + format);
+
+ // rename generated to desired
+ finalFile.renameTo(tarFile);
+
+ return tarFile;
} catch (Exception e) {
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/CreateWindowsExeLaunch4j.java b/src/main/java/io/github/fvarrui/javapackager/maven/CreateWindowsExeLaunch4j.java
index e596b031..f999376e 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/CreateWindowsExeLaunch4j.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/CreateWindowsExeLaunch4j.java
@@ -14,14 +14,18 @@
import java.util.List;
import java.util.stream.Collectors;
+import net.jsign.WindowsSigner;
import org.apache.commons.lang3.StringUtils;
import org.twdata.maven.mojoexecutor.MojoExecutor.Element;
+import io.github.fvarrui.javapackager.model.Arch;
import io.github.fvarrui.javapackager.model.WindowsConfig;
+import io.github.fvarrui.javapackager.model.WindowsExeCreationTool;
import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe;
import io.github.fvarrui.javapackager.packagers.Context;
import io.github.fvarrui.javapackager.packagers.WindowsPackager;
import io.github.fvarrui.javapackager.utils.FileUtils;
+import io.github.fvarrui.javapackager.utils.Logger;
/**
* Creates Windows executable with Maven
@@ -29,7 +33,7 @@
public class CreateWindowsExeLaunch4j extends AbstractCreateWindowsExe {
public CreateWindowsExeLaunch4j() {
- super("launch4j");
+ super(WindowsExeCreationTool.launch4j);
}
@Override
@@ -45,15 +49,27 @@ protected File doApply(WindowsPackager packager) throws Exception {
String classpath = packager.getClasspath();
String jreMinVersion = packager.getJreMinVersion();
File jarFile = packager.getJarFile();
+ File appFolder = packager.getAppFolder();
+ Arch arch = packager.getArch();
createAssets(packager);
- String jarPath = winConfig.isWrapJar() ? getGenericJar().getAbsolutePath() : jarFile.getName();
-
- List optsElements = vmArgs.stream().map(arg -> element("opt", arg)).collect(Collectors.toList());
+ // warns about architecture
+ if (arch != Arch.x86) {
+ Logger.warn("Launch4J only can generate 32-bit executable");
+ }
- List jreElements = new ArrayList<>();
- jreElements.add(element("opts", optsElements.toArray(new Element[optsElements.size()])));
+ // copies JAR to app folder
+ String jarPath;
+ if (winConfig.isWrapJar()) {
+ jarPath = getGenericJar().getAbsolutePath();
+ } else {
+ FileUtils.copyFileToFolder(jarFile, appFolder);
+ jarPath = jarFile.getName();
+ }
+
+ List jreElements = new ArrayList<>();
+ jreElements.add(element("opts", vmArgs.stream().map(arg -> element("opt", arg)).toArray(Element[]::new)));
jreElements.add(element("path", bundleJre ? jreDirectoryName : "%JAVA_HOME%;%PATH%"));
if (!StringUtils.isBlank(jreMinVersion)) {
jreElements.add(element("minVersion", jreMinVersion));
@@ -74,7 +90,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
)
);
pluginConfig.add(element("chdir", useResourcesAsWorkingDir ? "." : ""));
- pluginConfig.add(element("jre", jreElements.toArray(new Element[jreElements.size()])));
+ pluginConfig.add(element("jre", jreElements.toArray(new Element[0])));
pluginConfig.add(
element("versionInfo",
element("fileVersion", winConfig.getFileVersion()),
@@ -99,17 +115,15 @@ protected File doApply(WindowsPackager packager) throws Exception {
plugin(
groupId("com.akathist.maven.plugins.launch4j"),
artifactId("launch4j-maven-plugin"),
- version("2.1.1")
+ version("2.4.1")
),
goal("launch4j"),
- configuration(pluginConfig.toArray(new Element[pluginConfig.size()])),
+ configuration(pluginConfig.toArray(new Element[0])),
Context.getMavenContext().getEnv()
);
-
- sign(getGenericExe(), packager);
-
+
FileUtils.copyFileToFile(getGenericExe(), executable);
-
+
} catch (Exception ex) {
throw new RuntimeException(ex);
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/CreateZipball.java b/src/main/java/io/github/fvarrui/javapackager/maven/CreateZipball.java
index 561b9620..ead5c201 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/CreateZipball.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/CreateZipball.java
@@ -35,8 +35,6 @@ public boolean skip(Packager packager) {
protected File doApply(Packager packager) {
File assetsFolder = packager.getAssetsFolder();
- String name = packager.getName();
- String version = packager.getVersion();
Platform platform = packager.getPlatform();
File outputDirectory = packager.getOutputDirectory();
@@ -47,7 +45,6 @@ protected File doApply(Packager packager) {
VelocityUtils.render(platform + "/assembly.xml.vtl", assemblyFile, packager);
// zip file name and format
- String finalName = name + "-" + version + "-" + platform;
String format = "zip";
// invokes plugin to assemble zipball and/or tarball
@@ -62,13 +59,23 @@ protected File doApply(Packager packager) {
element("outputDirectory", outputDirectory.getAbsolutePath()),
element("formats", element("format", format)),
element("descriptors", element("descriptor", assemblyFile.getAbsolutePath())),
- element("finalName", finalName),
element("appendAssemblyId", "false")
),
Context.getMavenContext().getEnv()
);
- return new File(outputDirectory, finalName + "." + format);
+ // gets generated filename
+ String finalName = Context.getMavenContext().getEnv().getMavenProject().getBuild().getFinalName();
+ File finalFile = new File(outputDirectory, finalName + "." + format);
+
+ // gets desired file name
+ String zipName = packager.getZipballName() != null ? packager.getZipballName() : finalName + "-" + platform;
+ File zipFile = new File(outputDirectory, zipName + "." + format);
+
+ // rename generated to desired
+ finalFile.renameTo(zipFile);
+
+ return zipFile;
} catch (Exception e) {
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/MavenContext.java b/src/main/java/io/github/fvarrui/javapackager/maven/MavenContext.java
index dfbcf2e0..91f6b757 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/MavenContext.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/MavenContext.java
@@ -1,12 +1,17 @@
package io.github.fvarrui.javapackager.maven;
import java.io.File;
+import java.time.Year;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.model.Organization;
import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
import org.twdata.maven.mojoexecutor.MojoExecutor.ExecutionEnvironment;
import io.github.fvarrui.javapackager.packagers.AbstractCreateWindowsExe;
import io.github.fvarrui.javapackager.packagers.Context;
+import io.github.fvarrui.javapackager.packagers.CreateWindowsExeWhy;
import io.github.fvarrui.javapackager.packagers.CreateWindowsExeWinRun4j;
import io.github.fvarrui.javapackager.packagers.Packager;
import io.github.fvarrui.javapackager.packagers.WindowsPackager;
@@ -23,6 +28,24 @@ public MavenContext(ExecutionEnvironment env, Log logger) {
super();
this.env = env;
this.logger = logger;
+
+ // initialize some default params on project (avoid launch4j-maven-plugin warnings)
+ MavenProject project = env.getMavenProject();
+ if (project.getOrganization() == null) {
+ project.setOrganization(new Organization());
+ }
+ // set default organization name
+ if (StringUtils.isBlank(project.getOrganization().getName())) {
+ project.getOrganization().setName(Packager.DEFAULT_ORGANIZATION_NAME);
+ }
+ // set default inception year
+ if (StringUtils.isBlank(project.getInceptionYear())) {
+ project.setInceptionYear(Year.now().toString());
+ }
+ // set default description
+ if (StringUtils.isBlank(project.getDescription())) {
+ project.setDescription(project.getArtifactId());
+ }
}
public ExecutionEnvironment getEnv() {
@@ -73,6 +96,7 @@ public File createWindowsExe(WindowsPackager packager) throws Exception {
AbstractCreateWindowsExe createWindowsExe;
switch (packager.getWinConfig().getExeCreationTool()) {
case launch4j: createWindowsExe = new CreateWindowsExeLaunch4j(); break;
+ case why: createWindowsExe = new CreateWindowsExeWhy(); break;
case winrun4j: createWindowsExe = new CreateWindowsExeWinRun4j(); break;
default: return null;
}
@@ -81,7 +105,5 @@ public File createWindowsExe(WindowsPackager packager) throws Exception {
}
return null;
}
-
-
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/PackageMojo.java b/src/main/java/io/github/fvarrui/javapackager/maven/PackageMojo.java
index d1cb0a07..78d54203 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/PackageMojo.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/PackageMojo.java
@@ -1,12 +1,14 @@
package io.github.fvarrui.javapackager.maven;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
import java.io.File;
import java.util.List;
import java.util.Map;
+import io.github.fvarrui.javapackager.model.*;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
@@ -18,13 +20,6 @@
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
-import io.github.fvarrui.javapackager.model.FileAssociation;
-import io.github.fvarrui.javapackager.model.LinuxConfig;
-import io.github.fvarrui.javapackager.model.MacConfig;
-import io.github.fvarrui.javapackager.model.Manifest;
-import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.model.Scripts;
-import io.github.fvarrui.javapackager.model.WindowsConfig;
import io.github.fvarrui.javapackager.packagers.Context;
import io.github.fvarrui.javapackager.packagers.Packager;
import io.github.fvarrui.javapackager.packagers.PackagerFactory;
@@ -72,6 +67,12 @@ public class PackageMojo extends AbstractMojo {
@Parameter(defaultValue = "true", property = "generateInstaller", required = false)
private Boolean generateInstaller;
+ /**
+ * Forces installer generation.
+ */
+ @Parameter(defaultValue = "false", property = "forceInstaller", required = false)
+ private Boolean forceInstaller;
+
/**
* Full path to your app main class.
*/
@@ -188,7 +189,7 @@ public class PackageMojo extends AbstractMojo {
private Platform platform;
/**
- * Defines PATH environment variable in GNU/Linux and Mac OS X startup scripts.
+ * Defines PATH environment variable in GNU/Linux and MacOS startup scripts.
*/
@Parameter(property = "envPath", required = false)
private String envPath;
@@ -226,7 +227,7 @@ public class PackageMojo extends AbstractMojo {
private LinuxConfig linuxConfig;
/**
- * Mac OS X specific config
+ * MacOS specific config
*/
@Parameter(property = "macConfig", required = false)
private MacConfig macConfig;
@@ -243,12 +244,18 @@ public class PackageMojo extends AbstractMojo {
@Parameter(defaultValue = "false", property = "createTarball", required = false)
private Boolean createTarball;
+ @Parameter(property = "tarballName", required = false)
+ private String tarballName;
+
/**
* Bundles app in a zipball file
*/
@Parameter(defaultValue = "false", property = "createZipball", required = false)
private Boolean createZipball;
+ @Parameter(property = "zipballName", required = false)
+ private String zipballName;
+
/**
* Extra properties for customized Velocity templates, accesible through '$this.extra' map.
*/
@@ -308,7 +315,19 @@ public class PackageMojo extends AbstractMojo {
*/
@Parameter(property = "scripts", required = false)
private Scripts scripts;
+
+ /**
+ * Architecture
+ */
+ @Parameter(property = "arch", required = false)
+ private Arch arch;
+ /**
+ * Templates configuration
+ */
+ @Parameter(property = "templates", required = false)
+ private List templates;
+
public void execute() throws MojoExecutionException {
Context.setContext(
@@ -327,18 +346,22 @@ public void execute() throws MojoExecutionException {
.additionalModulePaths(additionalModulePaths)
.additionalResources(additionalResources)
.administratorRequired(administratorRequired)
+ .arch(defaultIfNull(arch, Arch.getDefault()))
.assetsDir(assetsDir)
.bundleJre(bundleJre)
.classpath(classpath)
.copyDependencies(copyDependencies)
.createTarball(createTarball)
+ .tarballName(tarballName)
.createZipball(createZipball)
+ .zipballName(zipballName)
.customizedJre(customizedJre)
.description(description)
.displayName(displayName)
.envPath(envPath)
.extra(extra)
.fileAssociations(fileAssociations)
+ .forceInstaller(forceInstaller)
.generateInstaller(generateInstaller)
.iconFile(iconFile)
.jdkPath(jdkPath)
@@ -359,6 +382,7 @@ public void execute() throws MojoExecutionException {
.packagingJdk(packagingJdk)
.runnableJar(runnableJar)
.scripts(scripts)
+ .templates(templates)
.useResourcesAsWorkingDir(useResourcesAsWorkingDir)
.url(url)
.version(version)
diff --git a/src/main/java/io/github/fvarrui/javapackager/maven/ResolveLicenseFromPOM.java b/src/main/java/io/github/fvarrui/javapackager/maven/ResolveLicenseFromPOM.java
index d3498ae5..9316fa92 100644
--- a/src/main/java/io/github/fvarrui/javapackager/maven/ResolveLicenseFromPOM.java
+++ b/src/main/java/io/github/fvarrui/javapackager/maven/ResolveLicenseFromPOM.java
@@ -1,8 +1,9 @@
package io.github.fvarrui.javapackager.maven;
import java.io.File;
-import java.io.IOException;
import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
@@ -36,13 +37,13 @@ protected File doApply(Packager packager) {
String urlStr = null;
try {
urlStr = licenses.get(0).getUrl();
- URL licenseUrl = new URL(urlStr);
+ URL licenseUrl = new URI(urlStr).toURL();
licenseFile = new File(assetsFolder, "LICENSE");
FileUtils.downloadFromUrl(licenseUrl, licenseFile);
- } catch (MalformedURLException e) {
+ } catch (URISyntaxException | MalformedURLException e) {
Logger.error("Invalid license URL specified: " + urlStr);
licenseFile = null;
- } catch (IOException e) {
+ } catch (Exception e) {
Logger.error("Cannot download license from " + urlStr);
licenseFile = null;
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/Arch.java b/src/main/java/io/github/fvarrui/javapackager/model/Arch.java
new file mode 100644
index 00000000..b00887dc
--- /dev/null
+++ b/src/main/java/io/github/fvarrui/javapackager/model/Arch.java
@@ -0,0 +1,60 @@
+package io.github.fvarrui.javapackager.model;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.redline_rpm.header.Architecture;
+
+public enum Arch {
+ aarch64,
+ x64,
+ x86;
+
+ public static Arch getArch(String archString) {
+ switch (archString) {
+ case "x86":
+ case "i386":
+ case "i486":
+ case "i586":
+ case "i686":
+ return x86;
+ case "x86_64":
+ case "amd64":
+ return x64;
+ case "aarch64":
+ return aarch64;
+ default:
+ throw new IllegalArgumentException("Unknown architecture " + archString);
+ }
+ }
+
+ public static Arch getDefault() {
+ return getArch(SystemUtils.OS_ARCH);
+ }
+
+ public String toDebArchitecture() {
+ switch (this) {
+ case aarch64: return "arm64";
+ case x64: return "amd64";
+ case x86: return "i386";
+ default: return null;
+ }
+ }
+
+ public Architecture toRpmArchitecture() {
+ switch (this) {
+ case aarch64: return Architecture.AARCH64;
+ case x64: return Architecture.X86_64;
+ case x86: return Architecture.I386;
+ default: return null;
+ }
+ }
+
+ public String toMsiArchitecture() {
+ switch (this) {
+ case aarch64: return "arm64";
+ case x64: return "x64";
+ case x86: return "x86";
+ default: return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java b/src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java
index 4573e83b..db24419d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/FileAssociation.java
@@ -1,6 +1,10 @@
package io.github.fvarrui.javapackager.model;
-public class FileAssociation {
+import java.io.Serializable;
+
+public class FileAssociation implements Serializable {
+ private static final long serialVersionUID = -2777769245508048627L;
+
private String mimeType;
private String extension;
private String description;
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java b/src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java
index 6e126ffe..f52507fe 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/LinuxConfig.java
@@ -2,6 +2,11 @@
import java.io.File;
import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.ObjectUtils;
import io.github.fvarrui.javapackager.packagers.Packager;
@@ -10,11 +15,22 @@
*/
public class LinuxConfig implements Serializable {
private static final long serialVersionUID = -1238166997019141904L;
-
+
+ private List categories;
private boolean generateDeb = true;
private boolean generateRpm = true;
+ private boolean generateAppImage = true;
private File pngFile;
private boolean wrapJar = true;
+ private String installationPath;
+
+ public void setCategories(List categories) {
+ this.categories = categories;
+ }
+
+ public List getCategories() {
+ return categories;
+ }
public boolean isGenerateDeb() {
return generateDeb;
@@ -32,26 +48,43 @@ public void setGenerateRpm(boolean generateRpm) {
this.generateRpm = generateRpm;
}
+ public boolean isGenerateAppImage() {
+ return generateAppImage;
+ }
+
+ public void setGenerateAppImage(boolean generateAppImage) {
+ this.generateAppImage = generateAppImage;
+ }
+
public File getPngFile() {
- return pngFile;
+ return pngFile;
}
public void setPngFile(File pngFile) {
this.pngFile = pngFile;
}
-
+
public boolean isWrapJar() {
return wrapJar;
}
-
+
public void setWrapJar(boolean wrapJar) {
this.wrapJar = wrapJar;
}
+
+ public String getInstallationPath() {
+ return installationPath;
+ }
+
+ public void setInstallationPath(String installationPath) {
+ this.installationPath = installationPath;
+ }
@Override
public String toString() {
- return "LinuxConfig [generateDeb=" + generateDeb + ", generateRpm=" + generateRpm + ", pngFile=" + pngFile
- + ", wrapJar=" + wrapJar + "]";
+ return "LinuxConfig [categories=" + categories + ", generateDeb=" + generateDeb + ", generateRpm=" + generateRpm
+ + ", generateAppImage=" + generateAppImage + ", pngFile=" + pngFile + ", wrapJar=" + wrapJar
+ + ", installationPath=" + installationPath + "]";
}
/**
@@ -60,7 +93,8 @@ public String toString() {
* @param packager Packager
*/
public void setDefaults(Packager packager) {
- // nothing
+ this.setCategories((categories == null || categories.isEmpty()) ? Collections.singletonList("Utility") : categories);
+ this.setInstallationPath(ObjectUtils.defaultIfNull(installationPath, "/opt"));
}
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/MacConfig.java b/src/main/java/io/github/fvarrui/javapackager/model/MacConfig.java
index afe6b39d..2f28aec5 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/MacConfig.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/MacConfig.java
@@ -8,7 +8,7 @@
import io.github.fvarrui.javapackager.packagers.Packager;
/**
- * JavaPackager Mac OS specific configuration
+ * JavaPackager MacOS specific configuration
*/
public class MacConfig implements Serializable {
private static final long serialVersionUID = -2268944961932941577L;
@@ -33,8 +33,16 @@ public class MacConfig implements Serializable {
private String appId;
private String developerId = "-";
private File entitlements;
+ private File provisionProfile;
+ private File customLauncher;
+ private File customInfoPlist;
+ private File customRuntimeInfoPlist;
private boolean codesignApp = true;
+ private boolean notarizeApp = false;
+ private String keyChainProfile;
private InfoPlist infoPlist = new InfoPlist();
+ private boolean hardenedCodesign = true;
+ private MacStartup macStartup = MacStartup.SCRIPT;
public File getIcnsFile() {
return icnsFile;
@@ -188,6 +196,38 @@ public void setDeveloperId(String developerId) {
this.developerId = developerId;
}
+ public File getCustomLauncher() {
+ return customLauncher;
+ }
+
+ public void setCustomLauncher(File customLauncher) {
+ this.customLauncher = customLauncher;
+ }
+
+ public File getCustomInfoPlist() {
+ return customInfoPlist;
+ }
+
+ public void setCustomInfoPlist(File customInfoPlist) {
+ this.customInfoPlist = customInfoPlist;
+ }
+
+ public File getCustomRuntimeInfoPlist() {
+ return customRuntimeInfoPlist;
+ }
+
+ public void setCustomRuntimeInfoPlist(File customRuntimeInfoPlist) {
+ this.customRuntimeInfoPlist = customRuntimeInfoPlist;
+ }
+
+ public File getProvisionProfile() {
+ return provisionProfile;
+ }
+
+ public void setProvisionProfile(File provisionProfile) {
+ this.provisionProfile = provisionProfile;
+ }
+
public File getEntitlements() {
return entitlements;
}
@@ -204,6 +244,22 @@ public void setCodesignApp(boolean codesignApp) {
this.codesignApp = codesignApp;
}
+ public boolean isNotarizeApp() {
+ return notarizeApp;
+ }
+
+ public void setNotarizeApp(boolean notarizeApp) {
+ this.notarizeApp = notarizeApp;
+ }
+
+ public String getKeyChainProfile() {
+ return keyChainProfile;
+ }
+
+ public void setKeyChainProfile(String keyChainProfile) {
+ this.keyChainProfile = keyChainProfile;
+ }
+
public InfoPlist getInfoPlist() {
return infoPlist;
}
@@ -212,6 +268,22 @@ public void setInfoPlist(InfoPlist infoPlist) {
this.infoPlist = infoPlist;
}
+ public void setHardenedCodesign(boolean hardenedCodesign) {
+ this.hardenedCodesign = hardenedCodesign;
+ }
+
+ public boolean isHardenedCodesign() {
+ return hardenedCodesign;
+ }
+
+ public MacStartup getMacStartup() {
+ return macStartup;
+ }
+
+ public void setMacStartup(MacStartup macStartup) {
+ this.macStartup = macStartup;
+ }
+
@Override
public String toString() {
return "MacConfig [icnsFile=" + icnsFile + ", backgroundImage=" + backgroundImage + ", windowWidth="
@@ -220,11 +292,15 @@ public String toString() {
+ ", appsLinkIconX=" + appsLinkIconX + ", appsLinkIconY=" + appsLinkIconY + ", volumeIcon=" + volumeIcon
+ ", volumeName=" + volumeName + ", generateDmg=" + generateDmg + ", generatePkg=" + generatePkg
+ ", relocateJar=" + relocateJar + ", appId=" + appId + ", developerId=" + developerId
- + ", entitlements=" + entitlements + ", codesignApp=" + codesignApp + ", infoPlist=" + infoPlist + "]";
+ + ", entitlements=" + entitlements + ", provisionProfile=" + provisionProfile + ", customLauncher="
+ + customLauncher + ", customInfoPlist=" + customInfoPlist + ", customRuntimeInfoPlist="
+ + customRuntimeInfoPlist + ", codesignApp=" + codesignApp + ", notarizeApp=" + notarizeApp
+ + ", keyChainProfile=" + keyChainProfile + ", infoPlist=" + infoPlist + ", hardenedCodesign="
+ + hardenedCodesign + ", macStartup=" + macStartup + "]";
}
/**
- * Tests Mac OS X specific config and set defaults if not specified
+ * Tests MacOS specific config and set defaults if not specified
*
* @param packager Packager
*/
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/MacStartup.java b/src/main/java/io/github/fvarrui/javapackager/model/MacStartup.java
new file mode 100644
index 00000000..41aa26a5
--- /dev/null
+++ b/src/main/java/io/github/fvarrui/javapackager/model/MacStartup.java
@@ -0,0 +1,8 @@
+package io.github.fvarrui.javapackager.model;
+
+public enum MacStartup {
+ UNIVERSAL,
+ X86_64,
+ ARM64,
+ SCRIPT
+}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/Platform.java b/src/main/java/io/github/fvarrui/javapackager/model/Platform.java
index da856e1f..6fb5a1c6 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/Platform.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/Platform.java
@@ -23,4 +23,13 @@ public static Platform getCurrentPlatform() {
return null;
}
+ public static Platform getPlatform(String platformString) {
+ switch (platformString.toLowerCase()) {
+ case "linux": return linux;
+ case "darwin": return mac;
+ case "windows": return windows;
+ default: throw new IllegalArgumentException("Unknown platform " + platformString);
+ }
+ }
+
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/Template.java b/src/main/java/io/github/fvarrui/javapackager/model/Template.java
new file mode 100644
index 00000000..b06274ce
--- /dev/null
+++ b/src/main/java/io/github/fvarrui/javapackager/model/Template.java
@@ -0,0 +1,36 @@
+package io.github.fvarrui.javapackager.model;
+
+public class Template {
+
+ private String name;
+ private boolean bom = false;
+
+ public Template() {}
+
+ public Template(String name, boolean bom) {
+ this.name = name;
+ this.bom = bom;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isBom() {
+ return bom;
+ }
+
+ public void setBom(boolean bom) {
+ this.bom = bom;
+ }
+
+ @Override
+ public String toString() {
+ return "Template [name=" + name + ", bom=" + bom + "]";
+ }
+
+}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/WindowsConfig.java b/src/main/java/io/github/fvarrui/javapackager/model/WindowsConfig.java
index 8f7637e3..cdf040cd 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/WindowsConfig.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/WindowsConfig.java
@@ -1,5 +1,6 @@
package io.github.fvarrui.javapackager.model;
+import static io.github.fvarrui.javapackager.utils.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import java.io.File;
@@ -7,8 +8,9 @@
import java.util.LinkedHashMap;
import java.util.UUID;
+import org.apache.commons.collections4.MapUtils;
+
import io.github.fvarrui.javapackager.packagers.Packager;
-import io.github.fvarrui.javapackager.utils.ObjectUtils;
/**
* JavaPackager Windows specific configuration
@@ -30,6 +32,7 @@ public class WindowsConfig implements Serializable {
private String trademarks;
private String txtFileVersion;
private String txtProductVersion;
+ private String shortcutName;
private boolean disableDirPage = true;
private boolean disableProgramGroupPage = true;
private boolean disableFinishedPage = true;
@@ -153,6 +156,14 @@ public void setTxtProductVersion(String txtProductVersion) {
this.txtProductVersion = txtProductVersion;
}
+ public String getShortcutName() {
+ return shortcutName;
+ }
+
+ public void setShortcutName(String shortcutName) {
+ this.shortcutName = shortcutName;
+ }
+
public String getInternalName() {
return internalName;
}
@@ -312,6 +323,7 @@ public String toString() {
+ ", internalName=" + internalName + ", language=" + language + ", originalFilename=" + originalFilename
+ ", productName=" + productName + ", productVersion=" + productVersion + ", trademarks=" + trademarks
+ ", txtFileVersion=" + txtFileVersion + ", txtProductVersion=" + txtProductVersion
+ + ", shortcutName=" + shortcutName
+ ", disableDirPage=" + disableDirPage + ", disableProgramGroupPage=" + disableProgramGroupPage
+ ", disableFinishedPage=" + disableFinishedPage + ", disableRunAfterInstall=" + disableRunAfterInstall
+ ", disableWelcomePage=" + disableWelcomePage + ", createDesktopIconTask=" + createDesktopIconTask
@@ -327,18 +339,25 @@ public String toString() {
* @param packager Packager
*/
public void setDefaults(Packager packager) {
- this.setHeaderType(ObjectUtils.defaultIfNull(this.getHeaderType(), HeaderType.gui));
+ this.setHeaderType(defaultIfNull(this.getHeaderType(), HeaderType.gui));
this.setFileVersion(defaultIfBlank(this.getFileVersion(), "1.0.0.0"));
this.setTxtFileVersion(defaultIfBlank(this.getTxtFileVersion(), "" + packager.getVersion()));
this.setProductVersion(defaultIfBlank(this.getProductVersion(), "1.0.0.0"));
+ this.setShortcutName(defaultIfBlank(this.getShortcutName(), packager.getDisplayName()));
this.setTxtProductVersion(defaultIfBlank(this.getTxtProductVersion(), "" + packager.getVersion()));
this.setCompanyName(defaultIfBlank(this.getCompanyName(), packager.getOrganizationName()));
+ this.setTrademarks(defaultIfBlank(this.getTrademarks(), packager.getOrganizationName()));
this.setCopyright(defaultIfBlank(this.getCopyright(), packager.getOrganizationName()));
this.setFileDescription(defaultIfBlank(this.getFileDescription(), packager.getDescription()));
this.setProductName(defaultIfBlank(this.getProductName(), packager.getName()));
this.setInternalName(defaultIfBlank(this.getInternalName(), packager.getName()));
this.setOriginalFilename(defaultIfBlank(this.getOriginalFilename(), packager.getName() + ".exe"));
this.setMsiUpgradeCode(defaultIfBlank(this.getMsiUpgradeCode(), UUID.randomUUID().toString()));
+ // init setup languages
+ if (MapUtils.isEmpty(this.getSetupLanguages())) {
+ this.getSetupLanguages().put("english", "compiler:Default.isl");
+ this.getSetupLanguages().put("spanish", "compiler:Languages\\Spanish.isl");
+ }
}
-
+
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/WindowsExeCreationTool.java b/src/main/java/io/github/fvarrui/javapackager/model/WindowsExeCreationTool.java
index 9ca9f53b..96770f39 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/WindowsExeCreationTool.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/WindowsExeCreationTool.java
@@ -2,5 +2,6 @@
public enum WindowsExeCreationTool {
launch4j,
+ why,
winrun4j;
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/model/WindowsSigning.java b/src/main/java/io/github/fvarrui/javapackager/model/WindowsSigning.java
index e677fa18..f565b314 100644
--- a/src/main/java/io/github/fvarrui/javapackager/model/WindowsSigning.java
+++ b/src/main/java/io/github/fvarrui/javapackager/model/WindowsSigning.java
@@ -1,13 +1,16 @@
package io.github.fvarrui.javapackager.model;
import java.io.File;
+import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
/**
* Info needed for signing EXEs on Windows
*/
-public class WindowsSigning {
+public class WindowsSigning implements Serializable {
+
+ private static final long serialVersionUID = 2559089741502151307L;
private String storetype;
private File keystore;
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/AbstractCreateWindowsExe.java b/src/main/java/io/github/fvarrui/javapackager/packagers/AbstractCreateWindowsExe.java
index 6f7288a6..c70b026d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/AbstractCreateWindowsExe.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/AbstractCreateWindowsExe.java
@@ -3,20 +3,21 @@
import java.io.File;
import io.github.fvarrui.javapackager.model.Platform;
+import io.github.fvarrui.javapackager.model.WindowsExeCreationTool;
import io.github.fvarrui.javapackager.utils.FileUtils;
import io.github.fvarrui.javapackager.utils.VelocityUtils;
-public abstract class AbstractCreateWindowsExe extends WindowsArtifactGenerator {
+public abstract class AbstractCreateWindowsExe extends ArtifactGenerator {
- private File outputFolder;
+ private final File outputFolder;
private File genericManifest;
private File genericIcon;
private File genericJar;
private File genericExe;
- public AbstractCreateWindowsExe(String outputFolderName) {
- super("Windows EXE");
- this.outputFolder = new File(Context.getContext().getBuildDir(), outputFolderName);
+ public AbstractCreateWindowsExe(WindowsExeCreationTool tool) {
+ super(tool.toString());
+ this.outputFolder = new File(Context.getContext().getBuildDir(), tool.toString());
}
public File getGenericManifest() {
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/BundleJre.java b/src/main/java/io/github/fvarrui/javapackager/packagers/BundleJre.java
index b649623d..ba686814 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/BundleJre.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/BundleJre.java
@@ -4,23 +4,28 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.utils.CommandUtils;
import io.github.fvarrui.javapackager.utils.FileUtils;
import io.github.fvarrui.javapackager.utils.JDKUtils;
import io.github.fvarrui.javapackager.utils.Logger;
import io.github.fvarrui.javapackager.utils.VersionUtils;
+import static io.github.fvarrui.javapackager.utils.CommandUtils.execute;
+
+
/**
- * Bundles a Java Runtime Enrironment (JRE) with the app
+ * Bundles a Java Runtime Environment (JRE) with the app
*/
public class BundleJre extends ArtifactGenerator {
+ private static final String ALL_MODULES = "ALL-MODULE-PATH";
+
public BundleJre() {
super("JRE");
}
@@ -44,7 +49,6 @@ protected File doApply(Packager packager) throws Exception {
List requiredModules = packager.getModules();
List additionalModules = packager.getAdditionalModules();
List additionalModulePaths = packager.getAdditionalModulePaths();
-
File currentJdk = packager.getPackagingJdk();
Logger.infoIndent("Bundling JRE ... with " + currentJdk);
@@ -57,23 +61,32 @@ protected File doApply(Packager packager) throws Exception {
throw new Exception("'" + specificJreFolder + "' is not a directory!");
}
- // checks if the specified jre is valid (it looks for 'release' file into it, and if so, checks if it matches the right platform
+ // checks if the specified jre is valid (it looks for 'release' file into it, and if so, checks if it matches the right platform
+ boolean validJre = true;
if (!JDKUtils.isValidJRE(platform, specificJreFolder)) {
- Logger.warn("An invalid JRE may have been specified for '" + platform + "' platform: " + specificJreFolder + " ('release' file not found)");
-
- // try to fix the path to the JRE on MacOS adding Contents/Home to JRE path
+ // if platform is mac
if (platform.equals(Platform.mac)) {
+ // try to fix the path to the JRE on MacOS adding Contents/Home to JRE path
File fixedJreFolder = new File(specificJreFolder, "Contents/Home");
if (JDKUtils.isValidJRE(platform, fixedJreFolder)) {
specificJreFolder = fixedJreFolder;
- Logger.warn("Specified 'jrePath' fixed: " + specificJreFolder);
+ Logger.warn("Specified 'jrePath' fixed: " + specificJreFolder);
+ } else {
+ validJre = false;
}
+ } else {
+ validJre = false;
}
}
+ if (!validJre) {
+ Logger.warn("An invalid JRE may have been specified for '" + platform + "' platform: " + specificJreFolder);
+ } else if (JDKUtils.isJDK(specificJreFolder)) {
+ Logger.warn("Wow! Embedding a JDK instead of a JRE ... are you sure you want to do that?");
+ }
// removes old jre folder from bundle
if (destinationFolder.exists()) FileUtils.removeFolder(destinationFolder);
@@ -106,12 +119,12 @@ protected File doApply(Packager packager) throws Exception {
Logger.info("Creating customized JRE ...");
- // tests if specified JDK is for the same platform than target platform
+ // tests if specified JDK is for the same platform as target platform
if (!JDKUtils.isValidJDK(platform, jdkPath)) {
throw new Exception("Invalid JDK for platform '" + platform + "': " + jdkPath);
}
- String modules = getRequiredModules(libsFolder, customizedJre, jarFile, requiredModules, additionalModules, additionalModulePaths);
+ String modules = getRequiredModules(currentJdk, libsFolder, customizedJre, jarFile, requiredModules, additionalModules, additionalModulePaths);
Logger.info("Creating JRE with next modules included: " + modules);
@@ -123,20 +136,29 @@ protected File doApply(Packager packager) throws Exception {
Logger.info("Using " + modulesDir + " modules directory");
if (destinationFolder.exists()) FileUtils.removeFolder(destinationFolder);
+
+ // gets JDK release info
+ Map releaseMap = JDKUtils.getRelease(jdkPath);
+ String releaseInfo = "add:IMAGE_TYPE=\"JRE\":OS_ARCH=\"" + releaseMap.get("OS_ARCH") + "\":OS_NAME=\"" + releaseMap.get("OS_NAME") + "\"";
- String jlink = new File(currentJdk, "/bin/jlink").getAbsolutePath();
+ // full path to jlink command
+ File jlink = new File(currentJdk, "/bin/jlink");
+
+ List modulePaths = new ArrayList();
+ modulePaths.add(modulesDir);
+ modulePaths.addAll(additionalModulePaths);
// generates customized jre using modules
- CommandUtils.execute(
+ execute(
jlink,
- "--module-path", modulesDir,
- additionalModulePathsToParams(additionalModulePaths),
+ "--module-path=" + StringUtils.join(modulePaths, File.pathSeparator),
"--add-modules", modules,
"--output", destinationFolder,
"--no-header-files",
"--no-man-pages",
- "--strip-debug",
- "--compress=2"
+ "--strip-debug",
+ "--release-info", releaseInfo,
+ (VersionUtils.getJavaMajorVersion() < 21 ? "--compress=2" : null)
);
// sets execution permissions on executables in jre
@@ -145,13 +167,13 @@ protected File doApply(Packager packager) throws Exception {
}
- // removes jre/legal folder as it causes problems when codesigning from Mac OS
+ // removes jre/legal folder as it causes problems when codesigning from MacOS
File legalFolder = new File(destinationFolder, "legal");
if (legalFolder.exists()) {
FileUtils.removeFolder(legalFolder);
}
- // removes jre/man folder as it causes problems when codesigning from Mac OS
+ // removes jre/man folder as it causes problems when codesigning from MacOS
File manFolder = new File(destinationFolder, "man");
if (manFolder.exists()) {
FileUtils.removeFolder(manFolder);
@@ -164,7 +186,7 @@ protected File doApply(Packager packager) throws Exception {
}
// updates bundle jre property value, as this artifact generator could disable this option
- // (e.g. when bundling a jre from a different platform than the current one)
+ // (e.g., when bundling a jre from a different platform than the current one)
packager.bundleJre(bundleJre);
return destinationFolder;
@@ -182,21 +204,20 @@ protected File doApply(Packager packager) throws Exception {
* @return string containing a comma separated list with all needed modules
* @throws Exception Process failed
*/
- protected String getRequiredModules(File libsFolder, boolean customizedJre, File jarFile, List defaultModules, List additionalModules, List additionalModulePaths) throws Exception {
+ protected String getRequiredModules(File packagingJdk, File libsFolder, boolean customizedJre, File jarFile, List defaultModules, List additionalModules, List additionalModulePaths) throws Exception {
Logger.infoIndent("Getting required modules ... ");
- File jdeps = new File(System.getProperty("java.home"), "/bin/jdeps");
-
- File jarLibs = null;
- if (libsFolder != null && libsFolder.exists())
- jarLibs = new File(libsFolder, "*.jar");
- else
- Logger.warn("No dependencies found!");
+ File jdeps = new File(packagingJdk, "/bin/jdeps");
+ List modulePaths = getModulePaths(jarFile, libsFolder, additionalModulePaths);
List modulesList;
- if (customizedJre && defaultModules != null && !defaultModules.isEmpty()) {
+ if (!customizedJre) {
+
+ return ALL_MODULES;
+
+ } else if (defaultModules != null && !defaultModules.isEmpty()) {
modulesList =
defaultModules
@@ -204,18 +225,17 @@ protected String getRequiredModules(File libsFolder, boolean customizedJre, File
.map(module -> module.trim())
.collect(Collectors.toList());
- } else if (customizedJre && VersionUtils.getJavaMajorVersion() >= 13) {
+ } else if (VersionUtils.getJavaMajorVersion() >= 13) {
String modules =
- CommandUtils.execute(
- jdeps.getAbsolutePath(),
+ execute(
+ jdeps,
"-q",
"--multi-release", VersionUtils.getJavaMajorVersion(),
"--ignore-missing-deps",
"--print-module-deps",
- additionalModulePathsToParams(additionalModulePaths),
- jarLibs,
- jarFile
+ "--add-modules=ALL-MODULE-PATH",
+ "--module-path=" + StringUtils.join(modulePaths, File.pathSeparator)
);
modulesList =
@@ -225,18 +245,17 @@ protected String getRequiredModules(File libsFolder, boolean customizedJre, File
.filter(module -> !module.isEmpty())
.collect(Collectors.toList());
- } else if (customizedJre && VersionUtils.getJavaMajorVersion() >= 9) {
+ } else if (VersionUtils.getJavaMajorVersion() >= 9) {
String modules =
- CommandUtils.execute(
+ execute(
jdeps.getAbsolutePath(),
"-q",
"--multi-release", VersionUtils.getJavaMajorVersion(),
"--ignore-missing-deps",
"--list-deps",
- additionalModulePathsToParams(additionalModulePaths),
- jarLibs,
- jarFile
+ "--add-modules=ALL-MODULE-PATH",
+ "--module-path=" + StringUtils.join(modulePaths, File.pathSeparator)
);
modulesList =
@@ -251,15 +270,15 @@ protected String getRequiredModules(File libsFolder, boolean customizedJre, File
} else {
- modulesList = Arrays.asList("ALL-MODULE-PATH");
+ modulesList = new ArrayList<>();
}
-
- modulesList.addAll(additionalModules);
if (modulesList.isEmpty()) {
Logger.warn("It was not possible to determine the necessary modules. All modules will be included");
- modulesList.add("ALL-MODULE-PATH");
+ modulesList.add(ALL_MODULES);
+ } else {
+ modulesList.addAll(additionalModules);
}
Logger.infoUnindent("Required modules found: " + modulesList);
@@ -267,23 +286,21 @@ protected String getRequiredModules(File libsFolder, boolean customizedJre, File
return StringUtils.join(modulesList, ",");
}
- private String [] additionalModulePathsToParams(List additionalModulePaths) {
-
- List additionalPaths = new ArrayList<>();
-
- additionalModulePaths
- .stream()
- .filter(path -> {
- if (path.exists()) return true;
- Logger.warn("Additional module path not found: " + path);
- return false;
- })
- .forEach(path -> {
- additionalPaths.add("--module-path");
- additionalPaths.add(path.toString());
- });
-
- return additionalPaths.toArray(new String[0]);
+ private List getModulePaths(File jarFile, File libsFolder, List additionalModulePaths) {
+ List modulePaths = new ArrayList<>();
+ modulePaths.add(jarFile);
+ modulePaths.add(libsFolder);
+ modulePaths.addAll(
+ additionalModulePaths
+ .stream()
+ .filter(path -> {
+ if (path.exists()) return true;
+ Logger.warn("Additional module path not found: " + path);
+ return false;
+ })
+ .collect(Collectors.toList())
+ );
+ return modulePaths;
}
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/Context.java b/src/main/java/io/github/fvarrui/javapackager/packagers/Context.java
index c239e7c1..e86ab4ed 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/Context.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/Context.java
@@ -22,6 +22,7 @@ public Context() {
// building tool independent generators
getInstallerGenerators(Platform.linux).add(new GenerateDeb());
getInstallerGenerators(Platform.linux).add(new GenerateRpm());
+ getInstallerGenerators(Platform.linux).add(new GenerateAppImage());
getInstallerGenerators(Platform.mac).add(new GenerateDmg());
getInstallerGenerators(Platform.mac).add(new GeneratePkg());
getInstallerGenerators(Platform.windows).add(new GenerateSetup());
@@ -78,7 +79,7 @@ public static boolean isGradle() {
return context instanceof GradleContext;
}
- public static MavenContext getMavenContext() {
+ public static MavenContext getMavenContext() {
return (MavenContext) context;
}
@@ -87,7 +88,10 @@ public static GradleContext getGradleContext() {
}
public File getDefaultToolchain() {
- return new File(System.getProperty("java.home")); // Use java.home as fallback
+ if (System.getenv("JAVA_HOME") != null) {
+ return new File(System.getenv("JAVA_HOME")); // Use JAVA_HOME as fallback
+ }
+ return new File(System.getProperty("java.home"));
}
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWhy.java b/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWhy.java
new file mode 100644
index 00000000..e689e166
--- /dev/null
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWhy.java
@@ -0,0 +1,88 @@
+package io.github.fvarrui.javapackager.packagers;
+
+import java.io.File;
+
+import io.github.fvarrui.javapackager.model.Platform;
+import io.github.fvarrui.javapackager.model.WindowsConfig;
+import io.github.fvarrui.javapackager.model.WindowsExeCreationTool;
+import io.github.fvarrui.javapackager.utils.FileUtils;
+import io.github.fvarrui.javapackager.utils.Logger;
+import io.github.fvarrui.javapackager.utils.RcEdit;
+import io.github.fvarrui.javapackager.utils.VelocityUtils;
+import net.jsign.WindowsSigner;
+
+/**
+ * Creates Windows executable with WinRun4j
+ */
+public class CreateWindowsExeWhy extends AbstractCreateWindowsExe {
+
+ public CreateWindowsExeWhy() {
+ super(WindowsExeCreationTool.why);
+ }
+
+ @Override
+ public boolean skip(WindowsPackager packager) {
+
+ if (!packager.getPlatform().isCurrentPlatform()) {
+ Logger.error(getArtifactName() + " cannot be generated with Why due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected File doApply(WindowsPackager packager) throws Exception {
+
+ File executable = packager.getExecutable();
+ File manifestFile = packager.getManifestFile();
+ File iconFile = packager.getIconFile();
+ File appFolder = packager.getAppFolder();
+ File jarFile = packager.getJarFile();
+ WindowsConfig winConfig = packager.getWinConfig();
+
+ if (winConfig.isWrapJar()) {
+ Logger.warn("'wrapJar' property ignored when building EXE with " + getArtifactName());
+ }
+
+ createAssets(packager);
+
+ // creates generic manifest
+ FileUtils.copyFileToFile(manifestFile, getGenericManifest());
+
+ // creates generic manifest
+ FileUtils.copyFileToFile(iconFile, getGenericIcon());
+
+ // creates generic exe
+ FileUtils.copyResourceToFile("/windows/JavaLauncher.exe", getGenericExe(), packager.getAssetsDir());
+
+ // generates ini file
+ File genericIni = new File(getOutputFolder(), "launcher.ini");
+ VelocityUtils.render("windows/why-ini.vtl", genericIni, packager);
+ Logger.info("INI file generated in " + genericIni.getAbsolutePath() + "!");
+
+ // set exe metadata with rcedit
+ RcEdit rcedit = new RcEdit(getOutputFolder());
+ rcedit.setIcon(getGenericExe(), getGenericIcon());
+ rcedit.setManifest(getGenericExe(), getGenericManifest());
+ rcedit.setFileVersion(getGenericExe(), winConfig.getFileVersion());
+ rcedit.setProductVersion(getGenericExe(), winConfig.getProductVersion());
+ rcedit.setVersionString(getGenericExe(), "FileDescription", winConfig.getFileDescription());
+ rcedit.setVersionString(getGenericExe(), "CompanyName", winConfig.getCompanyName());
+ rcedit.setVersionString(getGenericExe(), "InternalName", winConfig.getInternalName());
+ rcedit.setVersionString(getGenericExe(), "OriginalFilename", winConfig.getOriginalFilename());
+ rcedit.setVersionString(getGenericExe(), "ProductName", winConfig.getProductName());
+
+ // copies JAR to app folder
+ FileUtils.copyFileToFolder(jarFile, appFolder);
+
+ // copies ini file to app folder
+ FileUtils.copyFileToFolder(genericIni, appFolder);
+
+ // copies exe file to app folder with apps name
+ FileUtils.copyFileToFile(getGenericExe(), executable);
+
+ return createBootstrapScript(packager);
+ }
+
+}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWinRun4j.java b/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWinRun4j.java
index 92a2a6ff..221a3669 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWinRun4j.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/CreateWindowsExeWinRun4j.java
@@ -1,20 +1,21 @@
package io.github.fvarrui.javapackager.packagers;
import java.io.File;
-import java.io.FileOutputStream;
import java.nio.file.Path;
import java.util.Arrays;
-import java.util.List;
import java.util.Optional;
-import java.util.Properties;
+import net.jsign.WindowsSigner;
import org.apache.commons.lang3.StringUtils;
+import io.github.fvarrui.javapackager.model.Arch;
import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.utils.CommandUtils;
+import io.github.fvarrui.javapackager.model.WindowsConfig;
+import io.github.fvarrui.javapackager.model.WindowsExeCreationTool;
import io.github.fvarrui.javapackager.utils.FileUtils;
-import io.github.fvarrui.javapackager.utils.JarUtils;
+import io.github.fvarrui.javapackager.utils.JDKUtils;
import io.github.fvarrui.javapackager.utils.Logger;
+import io.github.fvarrui.javapackager.utils.RcEdit;
import io.github.fvarrui.javapackager.utils.VelocityUtils;
/**
@@ -28,7 +29,7 @@ public class CreateWindowsExeWinRun4j extends AbstractCreateWindowsExe {
};
public CreateWindowsExeWinRun4j() {
- super("winrun4j");
+ super(WindowsExeCreationTool.winrun4j);
}
@Override
@@ -50,12 +51,16 @@ protected File doApply(WindowsPackager packager) throws Exception {
File jarFile = packager.getJarFile();
File manifestFile = packager.getManifestFile();
File iconFile = packager.getIconFile();
- File libsFolder = packager.getLibsFolder();
File appFolder = packager.getAppFolder();
- String mainClass = packager.getMainClass();
File jreDestinationFolder = packager.getJreDestinationFolder();
boolean bundleJre = packager.getBundleJre();
String vmLocation = packager.getWinConfig().getVmLocation();
+ WindowsConfig winConfig = packager.getWinConfig();
+ Arch arch = packager.getArch();
+
+ if (winConfig.isWrapJar()) {
+ Logger.warn("'wrapJar' property ignored when building EXE with " + getArtifactName());
+ }
createAssets(packager);
@@ -65,12 +70,17 @@ protected File doApply(WindowsPackager packager) throws Exception {
// creates generic manifest
FileUtils.copyFileToFile(iconFile, getGenericIcon());
- // creates generic exe
- FileUtils.copyResourceToFile("/windows/WinRun4J64.exe", getGenericExe());
+ // checks if target architecture matches JRE arch
+ if (bundleJre && !JDKUtils.isValidJRE(Platform.windows, arch, packager.getJreDestinationFolder())) {
+ throw new Exception("Bundled JRE must match " + Platform.windows + " " + arch);
+ }
- // copies rcedit command line tool (needed to manipulate exe)
- File rcedit = new File(getOutputFolder(), "rcedit.exe");
- FileUtils.copyResourceToFile("/windows/rcedit-x64.exe", rcedit);
+ // creates generic exe
+ if (arch == Arch.x86) {
+ FileUtils.copyResourceToFile("/windows/WinRun4J.exe", getGenericExe());
+ } else {
+ FileUtils.copyResourceToFile("/windows/WinRun4J64.exe", getGenericExe());
+ }
// uses vmLocation only if a JRE is bundled
if (bundleJre) {
@@ -87,7 +97,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
} else {
- // searchs for valid jvm.dll file in JRE
+ // searchs for a valid jvm.dll file in JRE
Optional jvmDllFile = Arrays.asList(JVM_DLL_PATHS)
.stream()
.map(path -> new File(jreDestinationFolder, path))
@@ -113,43 +123,30 @@ protected File doApply(WindowsPackager packager) throws Exception {
}
+ // set exe metadata with rcedit
+ RcEdit rcedit = new RcEdit(getOutputFolder());
+ rcedit.setIcon(getGenericExe(), getGenericIcon());
+ rcedit.setManifest(getGenericExe(), getGenericManifest());
+ rcedit.setFileVersion(getGenericExe(), winConfig.getFileVersion());
+ rcedit.setProductVersion(getGenericExe(), winConfig.getProductVersion());
+ rcedit.setVersionString(getGenericExe(), "FileDescription", winConfig.getFileDescription());
+ rcedit.setVersionString(getGenericExe(), "CompanyName", winConfig.getCompanyName());
+ rcedit.setVersionString(getGenericExe(), "InternalName", winConfig.getInternalName());
+ rcedit.setVersionString(getGenericExe(), "OriginalFilename", winConfig.getOriginalFilename());
+ rcedit.setVersionString(getGenericExe(), "ProductName", winConfig.getProductName());
+
+ // copies JAR to libs folder
+ FileUtils.copyFileToFolder(jarFile, appFolder);
+
// generates ini file
File genericIni = new File(getOutputFolder(), "app.ini");
VelocityUtils.render("windows/ini.vtl", genericIni, packager);
Logger.info("INI file generated in " + genericIni.getAbsolutePath() + "!");
-
- // process EXE with rcedit-x64.exe
- CommandUtils.execute(rcedit, getGenericExe(), "--set-icon", getGenericIcon());
- CommandUtils.execute(rcedit, getGenericExe(), "--application-manifest", getGenericManifest());
-
- // creates libs folder if it doesn't exist
- if (libsFolder == null) {
- libsFolder = FileUtils.mkdir(appFolder, "libs");
- }
-
- // copies JAR to libs folder
- FileUtils.copyFileToFolder(jarFile, libsFolder);
-
- // copies winrun4j launcher helper library (needed to work around
- File winrun4jJar = new File(libsFolder, "winrun4j-launcher.jar");
- FileUtils.copyResourceToFile("/windows/winrun4j-launcher.jar", winrun4jJar);
-
- // generates winrun4j properties pointing to main class
- File propertiesFile = new File(getOutputFolder(), "winrun4j.properties");
- Properties properties = new Properties();
- properties.setProperty("main.class", mainClass);
- properties.store(new FileOutputStream(propertiesFile), "WinRun4J Helper Launcher Properties");
-
- // copies winrun4j properties to launcher jar
- JarUtils.addFileToJar(winrun4jJar, propertiesFile);
-
+
// copies ini file to app folder
File iniFile = new File(appFolder, name + ".ini");
FileUtils.copyFileToFile(genericIni, iniFile);
- // signs generated exe file
- sign(getGenericExe(), packager);
-
// copies exe file to app folder with apps name
FileUtils.copyFileToFile(getGenericExe(), executable);
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateAppImage.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateAppImage.java
new file mode 100644
index 00000000..5450e4b1
--- /dev/null
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateAppImage.java
@@ -0,0 +1,117 @@
+package io.github.fvarrui.javapackager.packagers;
+
+import io.github.fvarrui.javapackager.model.Platform;
+import io.github.fvarrui.javapackager.utils.CommandUtils;
+import io.github.fvarrui.javapackager.utils.FileUtils;
+import io.github.fvarrui.javapackager.utils.Logger;
+import io.github.fvarrui.javapackager.utils.VelocityUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+public class GenerateAppImage extends ArtifactGenerator {
+
+ private static final int IMAGETOOL_VERSION = 13;
+ private static final String IMAGETOOL_URL = "https://github.com/AppImage/AppImageKit/releases/download/" + IMAGETOOL_VERSION + "/appimagetool-%s.AppImage";
+
+ public GenerateAppImage() {
+ super("AppImage");
+ }
+
+ @Override
+ public boolean skip(LinuxPackager packager) {
+
+ if (!packager.getLinuxConfig().isGenerateAppImage()) {
+ return true;
+ }
+
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
+ Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
+ return true;
+ }
+
+ return false;
+
+ }
+
+ @Override
+ protected File doApply(LinuxPackager packager) throws Exception {
+
+ File appFolder = packager.getAppFolder();
+ File outputFolder = packager.getOutputDirectory();
+ String name = packager.getName();
+ File executable = packager.getExecutable();
+ File assetsFolder = packager.getAssetsFolder();
+ File iconFile = packager.getIconFile();
+
+ // output AppImage file
+ File appImage = new File(outputFolder, name + ".AppImage");
+
+ // AppDir folder
+ File appDir = new File(assetsFolder, "AppDir");
+
+ // gets/downloads AppImage tool
+ Logger.info("Getting appimagetool...");
+ File appImageTool = getAppImageTool(packager);
+ Logger.info("App image tool found! " + appImageTool);
+
+ // copies app folder to AppDir/usr/bin
+ FileUtils.copyFolderContentToFolder(appFolder, new File(appDir, "usr/bin"));
+
+ // generates AppImage desktop file from velocity template
+ File desktopFile = new File(appDir, name + ".desktop");
+ VelocityUtils.render("linux/desktop-appimage.vtl", desktopFile, packager);
+ Logger.info("Desktop file rendered in " + desktopFile.getAbsolutePath());
+
+ // creates AppRun symlink to startup script
+ Logger.info("Creating AppRun symlink to startup script...");
+ File appRun = new File(appDir, "AppRun");
+ FileUtils.createSymlink(appRun, new File("usr/bin", executable.getName()));
+
+ // creates AppRun symlink to startup script
+ Logger.info("Copying icon to AppDir ...");
+ FileUtils.copyFileToFolder(iconFile, appDir);
+
+ // runs appimagetool on appFolder
+ Logger.info("Running appimagetool on " + appFolder);
+ CommandUtils.execute(
+ appImageTool,
+ "--appimage-extract-and-run",
+ appDir,
+ appImage
+ );
+
+ Logger.info("Setting execution permissions to " + appImage);
+ appImage.setExecutable(true);
+
+ return appImage;
+ }
+
+ private File getAppImageTool(LinuxPackager packager) throws Exception {
+ File assetsFolder = packager.getAssetsFolder();
+ File appImageTool = new File(assetsFolder, "appimagetool");
+ if (!appImageTool.exists()) {
+ String imageToolUrl = String.format(IMAGETOOL_URL, getOSArch());
+ try {
+ FileUtils.downloadFromUrl(imageToolUrl, appImageTool);
+ } catch (IOException e) {
+ 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);
+ }
+ appImageTool.setExecutable(true);
+ }
+ return appImageTool;
+ }
+
+ private String getOSArch() {
+ switch (SystemUtils.OS_ARCH) {
+ case "amd64":
+ return "x86_64";
+ case "x86":
+ case "i386":
+ return "i686";
+ }
+ return SystemUtils.OS_ARCH;
+ }
+
+}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDeb.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDeb.java
index 5d959e4c..5003d74d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDeb.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDeb.java
@@ -22,7 +22,7 @@
*/
public class GenerateDeb extends ArtifactGenerator {
- private Console console;
+ private final Console console;
public GenerateDeb() {
super("DEB package");
@@ -63,6 +63,9 @@ protected File doApply(LinuxPackager packager) throws Exception {
String jreDirectoryName = packager.getJreDirectoryName();
File executable = packager.getExecutable();
File javaFile = new File(appFolder, jreDirectoryName + "/bin/java");
+ File mimeXmlFile = packager.getMimeXmlFile();
+ String installationPath = packager.getLinuxConfig().getInstallationPath();
+ String appPath = installationPath + "/" + name;
// generates desktop file from velocity template
File desktopFile = new File(assetsFolder, name + ".desktop");
@@ -79,20 +82,20 @@ protected File doApply(LinuxPackager packager) throws Exception {
// create data producers collections
- List conffilesProducers = new ArrayList<>();
+ List confFilesProducers = new ArrayList<>();
List dataProducers = new ArrayList<>();
// builds app folder data producer, except executable file and jre/bin/java
Mapper appFolderMapper = new Mapper();
appFolderMapper.setType("perm");
- appFolderMapper.setPrefix("/opt/" + name);
+ appFolderMapper.setPrefix(appPath);
appFolderMapper.setFileMode("644");
Data appFolderData = new Data();
appFolderData.setType("directory");
appFolderData.setSrc(appFolder);
- appFolderData.setExcludes(executable.getName() + (bundleJre ? "," + jreDirectoryName + "/bin/java" : ""));
+ appFolderData.setExcludes(executable.getName() + (bundleJre ? "," + jreDirectoryName + "/bin/java" + "," + jreDirectoryName + "/lib/jspawnhelper" : ""));
appFolderData.addMapper(appFolderMapper);
dataProducers.add(appFolderData);
@@ -101,12 +104,12 @@ protected File doApply(LinuxPackager packager) throws Exception {
Mapper executableMapper = new Mapper();
executableMapper.setType("perm");
- executableMapper.setPrefix("/opt/" + name);
+ executableMapper.setPrefix(appPath);
executableMapper.setFileMode("755");
Data executableData = new Data();
executableData.setType("file");
- executableData.setSrc(new File(appFolder.getAbsolutePath() + "/" + name));
+ executableData.setSrc(executable);
executableData.addMapper(executableMapper);
dataProducers.add(executableData);
@@ -124,6 +127,24 @@ protected File doApply(LinuxPackager packager) throws Exception {
dataProducers.add(desktopFileData);
+
+ // mime.xml file data producer
+
+ if (mimeXmlFile != null) {
+
+ Mapper mimeXmlFileMapper = new Mapper();
+ mimeXmlFileMapper.setType("perm");
+ mimeXmlFileMapper.setPrefix("/usr/share/mime/packages");
+
+ Data mimeXmlFileData = new Data();
+ mimeXmlFileData.setType("file");
+ mimeXmlFileData.setSrc(mimeXmlFile);
+ mimeXmlFileData.addMapper(mimeXmlFileMapper);
+
+ dataProducers.add(mimeXmlFileData);
+
+ }
+
// java binary file data producer
if (bundleJre) {
@@ -131,7 +152,7 @@ protected File doApply(LinuxPackager packager) throws Exception {
Mapper javaBinaryMapper = new Mapper();
javaBinaryMapper.setType("perm");
javaBinaryMapper.setFileMode("755");
- javaBinaryMapper.setPrefix("/opt/" + name + "/" + jreDirectoryName + "/bin");
+ javaBinaryMapper.setPrefix(appPath + "/" + jreDirectoryName + "/bin");
Data javaBinaryData = new Data();
javaBinaryData.setType("file");
@@ -140,17 +161,36 @@ protected File doApply(LinuxPackager packager) throws Exception {
dataProducers.add(javaBinaryData);
+ // set correct permissions on jre/lib/jspawnhelper
+
+ File jSpawnHelperFile = new File(appFolder, jreDirectoryName + "/lib/jspawnhelper");
+
+ if (jSpawnHelperFile.exists()) {
+
+ Mapper javaSpawnHelperMapper = new Mapper();
+ javaSpawnHelperMapper.setType("perm");
+ javaSpawnHelperMapper.setFileMode("755");
+ javaSpawnHelperMapper.setPrefix(appPath + "/" + jreDirectoryName + "/lib");
+
+ Data javaSpawnHelperData = new Data();
+ javaSpawnHelperData.setType("file");
+ javaSpawnHelperData.setSrc(jSpawnHelperFile);
+ javaSpawnHelperData.addMapper(javaSpawnHelperMapper);
+ dataProducers.add(javaSpawnHelperData);
+
+ }
+
}
// symbolic link in /usr/local/bin to app binary data producer
- DataProducer linkData = createLink("/usr/local/bin/" + name, "/opt/" + name + "/" + name);
+ DataProducer linkData = createLink("/usr/local/bin/" + executable.getName(), appPath + "/" + executable.getName());
dataProducers.add(linkData);
// builds deb file
- DebMaker debMaker = new DebMaker(console, dataProducers, conffilesProducers);
+ DebMaker debMaker = new DebMaker(console, dataProducers, confFilesProducers);
debMaker.setDeb(debFile);
debMaker.setControl(controlFile.getParentFile());
debMaker.setCompression("gzip");
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDmg.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDmg.java
index aaf3739e..c87db78d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDmg.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateDmg.java
@@ -5,6 +5,7 @@
import java.io.File;
import java.util.Arrays;
+import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
@@ -32,7 +33,7 @@ public boolean skip(MacPackager packager) {
return true;
}
- if (!packager.getPlatform().isCurrentPlatform()) {
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
return true;
}
@@ -82,10 +83,14 @@ protected File doApply(MacPackager packager) throws Exception {
Logger.info("Copying icon file: " + iconFile.getAbsolutePath());
File volumeIcon = (macConfig.getVolumeIcon() != null) ? macConfig.getVolumeIcon() : iconFile;
FileUtils.copyFileToFile(volumeIcon, new File(appFolder, ".VolumeIcon.icns"));
-
+
// creates image
Logger.info("Creating image: " + tempDmgFile.getAbsolutePath());
- execute("hdiutil", "create", "-srcfolder", appFolder, "-volname", volumeName, "-ov", "-fs", "HFS+", "-format", "UDRW", tempDmgFile);
+ String osArchitecture = System.getProperty("os.arch");
+ boolean isAarch64 = osArchitecture.equalsIgnoreCase("aarch64");
+ String fileSystem = isAarch64 ? "APFS" : "HFS+";
+ Logger.warn(osArchitecture + " architecture detected. Using " + fileSystem + " filesystem");
+ execute("hdiutil", "create", "-srcfolder", appFolder, "-volname", volumeName, "-ov", "-fs", fileSystem, "-format", "UDRW", tempDmgFile);
if (mountFolder.exists()) {
Logger.info("Unmounting volume: " + mountFolder);
@@ -95,13 +100,12 @@ protected File doApply(MacPackager packager) throws Exception {
// mounts image
Logger.info("Mounting image: " + tempDmgFile.getAbsolutePath());
String result = execute("hdiutil", "attach", "-readwrite", "-noverify", "-noautoopen", tempDmgFile);
- String deviceName = Arrays.asList(result.split("\n"))
- .stream()
- .filter(s -> s.contains(mountFolder.getAbsolutePath()))
- .map(s -> StringUtils.normalizeSpace(s))
- .map(s -> s.split(" ")[0])
- .findFirst().get();
- Logger.info("- Device name: " + deviceName);
+ Optional optDeviceName = Arrays.stream(result.split("\n"))
+ .filter(s -> s.contains(mountFolder.getAbsolutePath()))
+ .map(StringUtils::normalizeSpace)
+ .map(s -> s.split(" ")[0])
+ .findFirst();
+ optDeviceName.ifPresent(deviceName -> Logger.info("- Device name: " + deviceName));
// pause to prevent occasional "Can't get disk" (-1728) issues
// https://github.com/seltzered/create-dmg/commit/5fe7802917bb85b40c0630b026d33e421db914ea
@@ -126,10 +130,16 @@ protected File doApply(MacPackager packager) throws Exception {
// makes sure it's not world writeable and user readable
Logger.info("Fixing permissions...");
execute("chmod", "-Rf", "u+r,go-w", mountFolder);
-
- // makes the top window open itself on mount:
- Logger.info("Blessing ...");
- execute("bless", "--folder", mountFolder, "--openfolder", mountFolder);
+
+ if (!isAarch64) {
+ // makes the top window open itself on mount:
+ Logger.info("Blessing ...");
+ try {
+ execute("bless", "--folder", mountFolder, "--openfolder", mountFolder); }
+ catch (Exception e){
+ Logger.warn("Error blessing " + mountFolder + " due to: " + e.getMessage());
+ }
+ }
// tells the volume that it has a special file attribute
execute("SetFile", "-a", "C", mountFolder);
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsi.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsi.java
index 78bb35bf..373a3ee8 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsi.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsi.java
@@ -8,12 +8,13 @@
import io.github.fvarrui.javapackager.utils.Logger;
import io.github.fvarrui.javapackager.utils.VelocityUtils;
import io.github.fvarrui.javapackager.utils.XMLUtils;
+import net.jsign.WindowsSigner;
/**
- * Creates a MSI file including all app folder's content only for
+ * Creates an MSI file including all app folder's content only for
* Windows so app could be easily distributed
*/
-public class GenerateMsi extends WindowsArtifactGenerator {
+public class GenerateMsi extends ArtifactGenerator {
public GenerateMsi() {
super("MSI installer");
@@ -26,7 +27,7 @@ public boolean skip(WindowsPackager packager) {
return true;
}
- if (!packager.getPlatform().isCurrentPlatform()) {
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
return true;
}
@@ -50,7 +51,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
VelocityUtils.render("windows/wxs.vtl", wxsFile, packager);
Logger.info("WXS file generated in " + wxsFile + "!");
- // pretiffy wxs
+ // prettify wxs
XMLUtils.prettify(wxsFile);
// candle wxs file
@@ -62,7 +63,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
// lighting wxs file
Logger.info("Linking file " + wixobjFile);
File msiFile = new File(outputDirectory, name + "_" + version + ".msi");
- execute("light", "-spdb", "-out", msiFile, wixobjFile);
+ execute("light", "-sw1076", "-spdb", "-out", msiFile, wixobjFile);
// setup file
if (!msiFile.exists()) {
@@ -70,8 +71,8 @@ protected File doApply(WindowsPackager packager) throws Exception {
}
// sign installer
- sign(msiFile, packager);
-
+ WindowsSigner.sign(msiFile, packager.getDisplayName(), packager.getUrl(), packager.getWinConfig().getSigning());
+
return msiFile;
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsm.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsm.java
index f2a8c5f3..06b5a97b 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsm.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateMsm.java
@@ -9,10 +9,10 @@
import io.github.fvarrui.javapackager.utils.XMLUtils;
/**
- * Creates a MSI file including all app folder's content only for Windows so app
+ * Creates an MSI file including all app folder's content only for Windows so app
* could be easily distributed
*/
-public class GenerateMsm extends WindowsArtifactGenerator {
+public class GenerateMsm extends ArtifactGenerator {
public GenerateMsm() {
super("MSI merge module");
@@ -25,7 +25,7 @@ public boolean skip(WindowsPackager packager) {
return true;
}
- if (!packager.getPlatform().isCurrentPlatform()) {
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
return true;
}
@@ -50,7 +50,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
VelocityUtils.render("windows/msm.wxs.vtl", wxsFile, packager);
Logger.info("WXS file generated in " + wxsFile + "!");
- // pretiffy wxs
+ // prettify wxs
XMLUtils.prettify(wxsFile);
// candle wxs file
@@ -62,7 +62,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
// lighting wxs file
Logger.info("Linking file " + wixobjFile);
File msmFile = new File(outputDirectory, name + "_" + version + ".msm");
- CommandUtils.execute("light", "-spdb", "-out", msmFile, wixobjFile);
+ CommandUtils.execute("light", "-sw1076", "-spdb", "-out", msmFile, wixobjFile);
// setup file
if (!msmFile.exists()) {
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GeneratePkg.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GeneratePkg.java
index a70a531b..69e595be 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GeneratePkg.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GeneratePkg.java
@@ -23,7 +23,7 @@ public boolean skip(MacPackager packager) {
return true;
}
- if (!packager.getPlatform().isCurrentPlatform()) {
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
return true;
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateRpm.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateRpm.java
index 9f6e6538..e063a645 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateRpm.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateRpm.java
@@ -5,6 +5,7 @@
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import org.redline_rpm.Builder;
import org.redline_rpm.header.Architecture;
@@ -35,29 +36,34 @@ protected File doApply(LinuxPackager packager) throws Exception {
File appFolder = packager.getAppFolder();
String name = packager.getName();
- String version = packager.getVersion();
+ String version = packager.getVersion().replaceAll("-", "_");
String description = packager.getDescription();
String organizationName = packager.getOrganizationName();
File outputDirectory = packager.getOutputDirectory();
File executable = packager.getExecutable();
File assetsFolder = packager.getAssetsFolder();
String jreDirectoryName = packager.getJreDirectoryName();
-
+ Architecture arch = packager.getArch().toRpmArchitecture();
+ File mimeXmlFile = packager.getMimeXmlFile();
+ String installationPath = packager.getLinuxConfig().getInstallationPath();
+ String appPath = installationPath + "/" + name;
+
// generates desktop file from velocity template
File desktopFile = new File(assetsFolder, name + ".desktop");
VelocityUtils.render("linux/desktop.vtl", desktopFile, packager);
- Logger.info("Rendering desktop file to " + desktopFile.getAbsolutePath());
+ Logger.info("Desktop file rendered in " + desktopFile.getAbsolutePath());
// copies desktop file to app
FileUtils.copyFileToFolder(desktopFile, appFolder);
+ // creates RPM builder
Builder builder = new Builder();
builder.setType(RpmType.BINARY);
- builder.setPlatform(Architecture.X86_64, Os.LINUX);
+ builder.setPlatform(arch, Os.LINUX);
builder.setPackage(name, version, "1");
builder.setPackager(organizationName);
builder.setDescription(description);
- builder.setPrefixes("opt");
+ builder.setPrefixes(installationPath);
// list of files which needs execution permissions
List executionPermissions = new ArrayList<>();
@@ -66,17 +72,29 @@ protected File doApply(LinuxPackager packager) throws Exception {
executionPermissions.add(new File(appFolder, jreDirectoryName + "/lib/jspawnhelper"));
// add all app files
- addDirectoryTree(builder, "/opt", appFolder, executionPermissions);
+ addDirectory(builder, installationPath, appFolder, executionPermissions);
// link to desktop file
- builder.addLink("/usr/share/applications/" + desktopFile.getName(), "/opt/" + name + "/" + desktopFile.getName());
+ addLink(builder, "/usr/share/applications/" + desktopFile.getName(), appPath + "/" + desktopFile.getName());
+ // copy and link to mime.xml file
+ if (mimeXmlFile != null) {
+ FileUtils.copyFileToFolder(mimeXmlFile, appFolder);
+ addLink(builder, "/usr/share/mime/packages/" + mimeXmlFile.getName(), appPath + "/" + mimeXmlFile.getName());
+ }
+
// link to binary
- builder.addLink("/usr/local/bin/" + executable.getName(), "/opt/" + name + "/" + executable.getName());
+ addLink(builder, "/usr/local/bin/" + executable.getName(), appPath + "/" + executable.getName());
+ // add all app files
+ addDirectory(builder, installationPath, appFolder, executionPermissions);
+
+ // build RPM file
builder.build(outputDirectory);
- File originalRpm = new File(outputDirectory, name + "-" + version + "-1.x86_64.rpm");
+ // renames generated RPM file if created
+ String suffix = "-1." + arch + ".rpm";
+ File originalRpm = new File(outputDirectory, name + "-" + version + suffix);
File rpm = null;
if (originalRpm.exists()) {
rpm = new File(outputDirectory, name + "_" + version + ".rpm");
@@ -86,15 +104,27 @@ protected File doApply(LinuxPackager packager) throws Exception {
return rpm;
}
+
+ private void addLink(Builder builder, String path, String target) throws NoSuchAlgorithmException, IOException {
+ Logger.info("Adding link '" + path + "' to RPM builder targeting '" + target + "'");
+ builder.addLink(path, target);
+ }
+
+ private void addFile(Builder builder, String rootPath, File file, int mode) throws NoSuchAlgorithmException, IOException {
+ String filePath = rootPath + "/" + file.getName();
+ Logger.info("Adding file '" + file + "' to RPM builder as '" + filePath + "'");
+ builder.addFile(filePath, file, mode);
+ }
- private void addDirectoryTree(Builder builder, String parentPath, File root, List executionPermissions) throws NoSuchAlgorithmException, IOException {
- String rootPath = parentPath + "/" + root.getName();
- builder.addDirectory(rootPath);
- for (File f : root.listFiles()) {
+ private void addDirectory(Builder builder, String parentPath, File directory, List executionPermissions) throws NoSuchAlgorithmException, IOException {
+ String dirPath = parentPath + "/" + directory.getName();
+ Logger.info("Adding directory '" + directory + "' to RPM builder as '" + dirPath + "'");
+ builder.addDirectory(dirPath);
+ for (File f : Objects.requireNonNull(directory.listFiles())) {
if (f.isDirectory())
- addDirectoryTree(builder, parentPath + "/" + root.getName(), f, executionPermissions);
+ addDirectory(builder, dirPath, f, executionPermissions);
else {
- builder.addFile(rootPath + "/" + f.getName(), f, executionPermissions.contains(f) ? 0755 : 0644);
+ addFile(builder, dirPath, f, executionPermissions.contains(f) ? 0755 : 0644);
}
}
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateSetup.java b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateSetup.java
index d5fb190b..d78dca9d 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateSetup.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/GenerateSetup.java
@@ -2,20 +2,18 @@
import java.io.File;
-import org.apache.commons.lang3.StringUtils;
-
import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.model.Registry;
import io.github.fvarrui.javapackager.utils.CommandUtils;
import io.github.fvarrui.javapackager.utils.FileUtils;
import io.github.fvarrui.javapackager.utils.Logger;
import io.github.fvarrui.javapackager.utils.VelocityUtils;
+import net.jsign.WindowsSigner;
/**
* Creates a Setup file including all app folder's content only for
* Windows so app could be easily distributed
*/
-public class GenerateSetup extends WindowsArtifactGenerator {
+public class GenerateSetup extends ArtifactGenerator {
public GenerateSetup() {
super("Setup installer");
@@ -28,7 +26,7 @@ public boolean skip(WindowsPackager packager) {
return true;
}
- if (!packager.getPlatform().isCurrentPlatform()) {
+ if (!packager.getPlatform().isCurrentPlatform() && !packager.isForceInstaller()) {
Logger.warn(getArtifactName() + " cannot be generated due to the target platform (" + packager.getPlatform() + ") is different from the execution platform (" + Platform.getCurrentPlatform() + ")!");
return true;
}
@@ -44,12 +42,6 @@ protected File doApply(WindowsPackager packager) throws Exception {
String name = packager.getName();
File outputDirectory = packager.getOutputDirectory();
String version = packager.getVersion();
- Registry registry = packager.getWinConfig().getRegistry();
-
- // checks if registry entries' names are not empy
- if (registry.getEntries().stream().anyMatch(e -> StringUtils.isBlank(e.getKey()) || StringUtils.isBlank(e.getValueName()))) {
- throw new Exception("One or more registry entries have no key and/or value name");
- }
// copies ico file to assets folder
FileUtils.copyFileToFolder(iconFile, assetsFolder);
@@ -58,7 +50,7 @@ protected File doApply(WindowsPackager packager) throws Exception {
File issFile = new File(assetsFolder, name + ".iss");
VelocityUtils.render("windows/iss.vtl", issFile, packager);
- // generates windows installer with inno setup command line compiler
+ // generates Windows installer with inno setup command line compiler
CommandUtils.execute("iscc", "/O" + outputDirectory.getAbsolutePath(), "/F" + name + "_" + version, issFile);
// setup file
@@ -68,8 +60,8 @@ protected File doApply(WindowsPackager packager) throws Exception {
}
// sign installer
- sign(setupFile, packager);
-
+ WindowsSigner.sign(setupFile, packager.getDisplayName(), packager.getUrl(), packager.getWinConfig().getSigning());
+
return setupFile;
}
diff --git a/src/main/java/io/github/fvarrui/javapackager/packagers/MacPackager.java b/src/main/java/io/github/fvarrui/javapackager/packagers/MacPackager.java
index 25375f79..05cf90d6 100644
--- a/src/main/java/io/github/fvarrui/javapackager/packagers/MacPackager.java
+++ b/src/main/java/io/github/fvarrui/javapackager/packagers/MacPackager.java
@@ -1,35 +1,45 @@
package io.github.fvarrui.javapackager.packagers;
+import io.github.fvarrui.javapackager.model.MacStartup;
+import io.github.fvarrui.javapackager.model.Platform;
+import io.github.fvarrui.javapackager.utils.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+import org.codehaus.plexus.util.cli.CommandLineException;
+
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.SystemUtils;
-import org.codehaus.plexus.util.cli.CommandLineException;
-
-import io.github.fvarrui.javapackager.model.Platform;
-import io.github.fvarrui.javapackager.utils.CommandUtils;
-import io.github.fvarrui.javapackager.utils.FileUtils;
-import io.github.fvarrui.javapackager.utils.Logger;
-import io.github.fvarrui.javapackager.utils.VelocityUtils;
-import io.github.fvarrui.javapackager.utils.VersionUtils;
-import io.github.fvarrui.javapackager.utils.XMLUtils;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
/**
- * Packager for Mac OS X
+ * Packager for MacOS
*/
public class MacPackager extends Packager {
-
+
private File appFile;
private File contentsFolder;
private File resourcesFolder;
private File javaFolder;
private File macOSFolder;
+ private File jreBundleFolder;
+ public MacPackager() {
+ super();
+ platform(Platform.mac);
+ }
+
public File getAppFile() {
return appFile;
}
@@ -38,18 +48,18 @@ public File getAppFile() {
public void doInit() throws Exception {
this.macConfig.setDefaults(this);
-
- // FIX useResourcesAsWorkingDir=false doesn't work fine on Mac OS (option disabled)
+
+ // FIX useResourcesAsWorkingDir=false doesn't work fine on Mac OS (option disabled)
if (!this.isUseResourcesAsWorkingDir()) {
this.useResourcesAsWorkingDir = true;
Logger.warn("'useResourcesAsWorkingDir' property disabled on Mac OS (useResourcesAsWorkingDir is always true)");
}
-
+
}
@Override
protected void doCreateAppStructure() throws Exception {
-
+
// initializes the references to the app structure folders
this.appFile = new File(appFolder, name + ".app");
this.contentsFolder = new File(appFile, "Contents");
@@ -58,85 +68,131 @@ protected void doCreateAppStructure() throws Exception {
this.macOSFolder = new File(contentsFolder, "MacOS");
// makes dirs
-
+
FileUtils.mkdir(this.appFile);
Logger.info("App file folder created: " + appFile.getAbsolutePath());
-
+
FileUtils.mkdir(this.contentsFolder);
Logger.info("Contents folder created: " + contentsFolder.getAbsolutePath());
-
+
FileUtils.mkdir(this.resourcesFolder);
Logger.info("Resources folder created: " + resourcesFolder.getAbsolutePath());
-
+
FileUtils.mkdir(this.javaFolder);
Logger.info("Java folder created: " + javaFolder.getAbsolutePath());
-
+
FileUtils.mkdir(this.macOSFolder);
Logger.info("MacOS folder created: " + macOSFolder.getAbsolutePath());
// sets common folders
this.executableDestinationFolder = macOSFolder;
this.jarFileDestinationFolder = javaFolder;
- this.jreDestinationFolder = new File(contentsFolder, "PlugIns/" + jreDirectoryName + "/Contents/Home");
+ this.jreBundleFolder = new File(contentsFolder, "PlugIns/" + jreDirectoryName + ".jre");
+ this.jreDestinationFolder = new File(jreBundleFolder, "Contents/Home");
this.resourcesDestinationFolder = resourcesFolder;
}
-
+
/**
* Creates a native MacOS app bundle
*/
@Override
public File doCreateApp() throws Exception {
-
+ if(bundleJre) {
+ processRuntimeInfoPlistFile();
+ }
// copies jarfile to Java folder
FileUtils.copyFileToFolder(jarFile, javaFolder);
+
+ processStartupScript();
+
+ processClasspath();
+
+ processInfoPlistFile();
+
+ processProvisionProfileFile();
+
+ codesign();
+
+ notarize();
+
+ return appFile;
+ }
+
+ private void processStartupScript() throws Exception {
if (this.administratorRequired) {
+ // We need a helper script ("startup") in this case,
+ // which invokes the launcher script/ executable with administrator rights.
+ // TODO: admin script depends on launcher file name 'universalJavaApplicationStub'
+
// sets startup file
- this.executable = new File(macOSFolder, "startup");
-
+ this.executable = new File(macOSFolder, "startup");
+
// creates startup file to boot java app
VelocityUtils.render("mac/startup.vtl", executable, this);
- executable.setExecutable(true, false);
- Logger.info("Startup script file created in " + executable.getAbsolutePath());
-
- } else {
-
- // sets startup file
- this.executable = new File(macOSFolder, "universalJavaApplicationStub");
- Logger.info("Using " + executable.getAbsolutePath() + " as startup script");
- }
-
- // copies universalJavaApplicationStub startup file to boot java app
- File appStubFile = new File(macOSFolder, "universalJavaApplicationStub");
- FileUtils.copyResourceToFile("/mac/universalJavaApplicationStub", appStubFile, true);
- FileUtils.processFileContent(appStubFile, content -> {
- if (!macConfig.isRelocateJar()) {
- content = content.replaceAll("/Contents/Resources/Java", "/Contents/Resources");
+ } else {
+
+ File launcher = macConfig.getCustomLauncher();
+ if (launcher != null && launcher.canRead() && launcher.isFile()){
+ FileUtils.copyFileToFolder(launcher, macOSFolder);
+ this.executable = new File(macOSFolder, launcher.getName());
+ } else {
+ this.executable = preparePrecompiledStartupStub();
}
- content = content.replaceAll("\\$\\{info.name\\}", this.name);
- return content;
- });
- appStubFile.setExecutable(true, false);
+ }
- // process classpath
+ executable.setExecutable(true, false);
+ Logger.info("Startup script file created in " + executable.getAbsolutePath());
+ }
+
+ private void processClasspath() {
+ // TODO: Why are we doing this here? I do not see any usage of 'classpath' or 'classpaths' here.
classpath = (this.macConfig.isRelocateJar() ? "Java/" : "") + this.jarFile.getName() + (classpath != null ? ":" + classpath : "");
classpaths = Arrays.asList(classpath.split("[:;]"));
if (!isUseResourcesAsWorkingDir()) {
- classpaths = classpaths.stream().map(cp -> new File(cp).isAbsolute() ? cp : "$ResourcesFolder/" + cp).collect(Collectors.toList());
+ classpaths = classpaths
+ .stream()
+ .map(cp -> new File(cp).isAbsolute() ? cp : "$ResourcesFolder/" + cp)
+ .collect(Collectors.toList());
}
classpath = StringUtils.join(classpaths, ":");
+ }
- // creates and write the Info.plist file
+ /**
+ * Creates and writes the Info.plist file if no custom file is specified.
+ * @throws Exception if anything goes wrong
+ */
+ private void processInfoPlistFile() throws Exception {
File infoPlistFile = new File(contentsFolder, "Info.plist");
- VelocityUtils.render("mac/Info.plist.vtl", infoPlistFile, this);
- XMLUtils.prettify(infoPlistFile);
+ if(macConfig.getCustomInfoPlist() != null && macConfig.getCustomInfoPlist().isFile() && macConfig.getCustomInfoPlist().canRead()){
+ FileUtils.copyFileToFile(macConfig.getCustomInfoPlist(), infoPlistFile);
+ } else {
+ VelocityUtils.render("mac/Info.plist.vtl", infoPlistFile, this);
+ XMLUtils.prettify(infoPlistFile);
+ }
Logger.info("Info.plist file created in " + infoPlistFile.getAbsolutePath());
+ }
- // codesigns app folder
+ /**
+ * Creates and writes the Info.plist inside the JRE if no custom file is specified.
+ * @throws Exception if anything goes wrong
+ */
+ private void processRuntimeInfoPlistFile() throws Exception {
+ File infoPlistFile = new File(jreBundleFolder, "Contents/Info.plist");
+ if(macConfig.getCustomRuntimeInfoPlist() != null && macConfig.getCustomRuntimeInfoPlist().isFile() && macConfig.getCustomRuntimeInfoPlist().canRead()){
+ FileUtils.copyFileToFile(macConfig.getCustomRuntimeInfoPlist(), infoPlistFile);
+ } else {
+ VelocityUtils.render("mac/RuntimeInfo.plist.vtl", infoPlistFile, this);
+ XMLUtils.prettify(infoPlistFile);
+ }
+ Logger.info("RuntimeInfo.plist file created in " + infoPlistFile.getAbsolutePath());
+ }
+
+ private void codesign() throws Exception {
if (!Platform.mac.isCurrentPlatform()) {
Logger.warn("Generated app could not be signed due to current platform is " + Platform.getCurrentPlatform());
} else if (!getMacConfig().isCodesignApp()) {
@@ -144,38 +200,167 @@ public File doCreateApp() throws Exception {
} else {
codesign(this.macConfig.getDeveloperId(), this.macConfig.getEntitlements(), this.appFile);
}
-
- return appFile;
}
- private void codesign(String developerId, File entitlements, File appFile) throws IOException, CommandLineException {
-
- List flags = new ArrayList<>();
- if (VersionUtils.compareVersions("10.13.6", SystemUtils.OS_VERSION) >= 0) {
- flags.add("runtime"); // enable hardened runtime if Mac OS version >= 10.13.6
+ private void notarize() throws Exception {
+ if (!Platform.mac.isCurrentPlatform()) {
+ Logger.warn("Generated app could not be notarized due to current platform is " + Platform.getCurrentPlatform());
+ } else if (!getMacConfig().isCodesignApp()) {
+ Logger.warn("App codesigning disabled. Cannot notarize unsigned app");
+ } else if (!getMacConfig().isNotarizeApp()) {
+ Logger.warn("App notarization disabled");
} else {
- Logger.warn("Mac OS version detected: " + SystemUtils.OS_VERSION + " ... hardened runtime disabled!");
+ notarize(this.macConfig.getKeyChainProfile(), this.appFile);
}
-
- List