├── .gitattributes ├── .github └── workflows │ ├── build-runtime-plugin.yml │ └── publish-plugin.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── doc ├── examples.adoc ├── index.adoc ├── introduction.adoc └── user_guide.adoc ├── ghpages ├── CNAME ├── index.html └── releases │ └── latest │ └── index.html ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── license-header.txt ├── publish.sh ├── settings.gradle └── src ├── main ├── groovy │ └── org │ │ └── beryx │ │ └── runtime │ │ ├── BaseTask.groovy │ │ ├── JPackageImageTask.groovy │ │ ├── JPackageTask.groovy │ │ ├── JreTask.groovy │ │ ├── RuntimePlugin.groovy │ │ ├── RuntimeTask.groovy │ │ ├── RuntimeZipTask.groovy │ │ ├── SuggestModulesTask.groovy │ │ ├── data │ │ ├── CdsData.groovy │ │ ├── JPackageData.groovy │ │ ├── JPackageTaskData.groovy │ │ ├── JreTaskData.groovy │ │ ├── LauncherData.groovy │ │ ├── RuntimePluginExtension.groovy │ │ ├── RuntimeTaskData.groovy │ │ ├── RuntimeZipTaskData.groovy │ │ ├── SuggestModulesData.groovy │ │ └── TargetPlatform.groovy │ │ ├── impl │ │ ├── BaseTaskImpl.groovy │ │ ├── JPackageImageTaskImpl.groovy │ │ ├── JPackageTaskImpl.groovy │ │ ├── JreTaskImpl.groovy │ │ ├── RuntimeTaskImpl.groovy │ │ ├── RuntimeZipTaskImpl.groovy │ │ └── SuggestModulesTaskImpl.groovy │ │ └── util │ │ ├── JdkUtil.groovy │ │ ├── ModuleManager.groovy │ │ ├── PackageCollection.groovy │ │ ├── PackageUseScanner.groovy │ │ ├── SourceCodeRunner.groovy │ │ ├── SuggestedModulesBuilder.groovy │ │ └── Util.groovy └── resources │ ├── unixScriptTemplate.txt │ ├── windowsScriptTemplate.txt │ └── windowsScriptTemplateJavaw.txt └── test ├── groovy └── org │ └── beryx │ └── runtime │ ├── ModuleManagerSpec.groovy │ ├── PackageUseScannerSpec.groovy │ ├── RuntimePluginSpec.groovy │ └── SuggestModulesSpec.groovy └── resources ├── hello-log4j-2.9.0 ├── build.gradle ├── settings.gradle └── src │ └── main │ ├── java │ └── org │ │ └── example │ │ └── runtime │ │ └── hello │ │ └── Hello.java │ └── resources │ └── log4j2.xml ├── hello-logback ├── build.gradle ├── settings.gradle └── src │ └── main │ ├── java │ └── org │ │ └── example │ │ └── runtime │ │ └── Hello.java │ └── resources │ └── logback.xml └── libs ├── ecj-3.13.102.jar ├── java.base.jmod ├── java.naming.jmod ├── java.xml.jmod ├── jaxb-api-2.3.0.jar ├── logback-classic-1.3.0-alpha4.jar ├── logback-core-1.3.0-alpha4.jar ├── multi-release-hello.jar └── slf4j-api-1.8.0-beta2.jar /.gitattributes: -------------------------------------------------------------------------------- 1 | gradlew eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/build-runtime-plugin.yml: -------------------------------------------------------------------------------- 1 | name: Java 11 Gradle CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | if: github.ref == 'refs/heads/master' && github.event_name == 'push' 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/cache@v1 12 | with: 13 | path: ~/.gradle/caches 14 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 15 | restore-keys: | 16 | ${{ runner.os }}-gradle- 17 | - name: Set up JDK 11 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 11 21 | - name: Grant execute permission for gradlew 22 | run: chmod +x gradlew 23 | - name: Grant execute permission for publish.sh 24 | run: chmod +x publish.sh 25 | - name: Build with Gradle 26 | run: ./gradlew -is build groovydoc asciidoc --scan 27 | - name: Run publish if necessary 28 | env: # Or as an environment variable 29 | GRGIT_USER: ${{ secrets.GRGIT_USER }} 30 | run: ./publish.sh 31 | -------------------------------------------------------------------------------- /.github/workflows/publish-plugin.yml: -------------------------------------------------------------------------------- 1 | name: Publish to the Gradle Plugin Portal 2 | 3 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up JDK 11 11 | uses: actions/setup-java@v3 12 | with: 13 | java-version: '11' 14 | distribution: 'temurin' 15 | - name: Publish Gradle Plugins 16 | uses: gradle/gradle-build-action@v2 17 | with: 18 | arguments: | 19 | assemble 20 | publishPlugins 21 | -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} 22 | -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # directory names ending with / will be excluded from all IDEA modules (see ide.gradle) 2 | 3 | gradle-local.properties 4 | gpg/ 5 | .gradle/ 6 | build/ 7 | *.class 8 | *.log 9 | *.settings.xml 10 | 11 | # Eclipse 12 | .classpath 13 | .project 14 | .settings/ 15 | bin/ 16 | 17 | # IDEA 18 | *.iml 19 | *.ipr 20 | *.iws 21 | .idea/ 22 | out/ 23 | 24 | hs_err_pid* 25 | _ignore* 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 2 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/beryx/badass-runtime-plugin/blob/master/LICENSE) 3 | [![Build Status](https://img.shields.io/github/workflow/status/beryx/badass-runtime-plugin/Java%2011%20Gradle%20CI)](https://github.com/beryx/badass-runtime-plugin/actions?query=workflow%3A%22Java+11+Gradle+CI%22) 4 | 5 | ## Badass Runtime Plugin ## 6 | 7 | ##### NOTE: Looking for co-maintainers - see [this issue](https://github.com/beryx/badass-runtime-plugin/issues/135). ##### 8 | 9 | Using this Gradle plugin you can create custom runtime images for non-modularized applications. 10 | The plugin also lets you create an application installer with the [jpackage](https://openjdk.java.net/jeps/392) tool. 11 | 12 | 13 | :bulb: For modularized applications use the [Badass-JLink plugin](https://badass-jlink-plugin.beryx.org/releases/latest/). 14 | 15 | The plugin offers several tasks, such as: `runtime`, `runtimeZip`, `suggestModules`, or `jpackage`. 16 | It also adds an extension with the name `runtime` to let you configure various aspects of its operation. 17 | A simple example configuration is shown below: 18 | 19 | ``` 20 | runtime { 21 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] 22 | modules = ['java.naming', 'java.xml'] 23 | } 24 | ``` 25 | 26 | The following projects illustrate how to use this plugin to create custom runtime images and/or platform-specific installers: 27 | - [badass-runtime-example](https://github.com/beryx-gist/badass-runtime-example) - a 'Hello world' application using slf4j and logback. 28 | - [badass-runtime-example-javafx](https://github.com/beryx-gist/badass-runtime-example-javafx) - a 'Hello world' JavaFX application. 29 | - [badass-runtime-example-javafx-cds](https://github.com/beryx-gist/badass-runtime-example-javafx-cds) - a 'Hello world' JavaFX application with Class Data Sharing. 30 | - [badass-runtime-example-kotlin-tornadofx](https://github.com/beryx-gist/badass-runtime-example-kotlin-tornadofx) - a 'Hello world' application written in Kotlin using [tornadofx](https://github.com/edvin/tornadofx). 31 | - [badass-runtime-spring-petclinic](https://github.com/beryx-gist/badass-runtime-spring-petclinic) - creates a custom runtime image of the [Spring PetClinic](https://github.com/spring-projects/spring-petclinic) application. 32 | - [badass-runtime-pacman](https://github.com/beryx-gist/badass-runtime-pacman) - creates a custom runtime image and an application installer for the Pacman game available in the [FXGLGames](https://github.com/AlmasB/FXGLGames) repository. 33 | - [bespoke-images](https://github.com/PaulWinstone/demoModule) - creates multiple executable images, each one customized for a different client. 34 | 35 | 36 | 37 | ### Please [read the documentation](https://badass-runtime-plugin.beryx.org/releases/latest/) before using this plugin. 38 | 39 | See the [list of all releases](https://github.com/beryx/badass-runtime-plugin/blob/gh-pages/releases.md) if you use an older version of this plugin. 40 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.text.SimpleTemplateEngine 2 | import org.kohsuke.github.* 3 | 4 | buildscript { 5 | repositories { 6 | mavenCentral() 7 | maven { 8 | url "https://plugins.gradle.org/m2/" 9 | } 10 | } 11 | dependencies { 12 | classpath 'org.kohsuke:github-api:1.94' 13 | } 14 | } 15 | 16 | plugins { 17 | id 'java-gradle-plugin' 18 | id 'groovy' 19 | id 'maven-publish' 20 | id 'com.github.johnrengelman.shadow' version '7.1.2' 21 | id "com.gradle.plugin-publish" version "1.1.0" 22 | id "com.github.ethankhall.semantic-versioning" version "1.1.0" 23 | id "com.github.ben-manes.versions" version "0.44.0" 24 | id 'com.github.jk1.dependency-license-report' version '2.1' 25 | id "com.github.hierynomus.license" version "0.16.1" 26 | id 'org.asciidoctor.jvm.convert' version '3.3.2' 27 | id "org.ajoberstar.git-publish" version "4.1.1" 28 | } 29 | 30 | if(JavaVersion.current() != JavaVersion.VERSION_11) { 31 | throw new GradleException("Please use Java 11 to execute this Gradle build") 32 | } 33 | 34 | project.version.with { 35 | major = badassRuntimePluginVersionMajor as int 36 | minor= badassRuntimePluginVersionMinor as int 37 | patch = badassRuntimePluginVersionPatch as int 38 | if (project.hasProperty('badassRuntimePluginVersionLabel')) { 39 | preRelease = badassRuntimePluginVersionLabel 40 | } 41 | releaseBuild = Boolean.valueOf(badassRuntimePluginReleaseBuild) 42 | } 43 | ext.badassRuntimePluginVersion = project.version as String 44 | ext.badassRuntimePluginTag = Boolean.valueOf(badassRuntimePluginReleaseBuild) ? "v$ext.badassRuntimePluginVersion" : 'master' 45 | 46 | group = 'org.beryx' 47 | version = badassRuntimePluginVersion 48 | 49 | ext.asmVersion = '9.4' 50 | 51 | repositories { 52 | mavenCentral() 53 | } 54 | 55 | sourceCompatibility = 1.8 56 | 57 | def defaultEncoding = 'UTF-8' 58 | [compileJava, compileTestJava]*.options*.encoding = defaultEncoding 59 | 60 | [compileGroovy, compileTestGroovy]*.options*.encoding = defaultEncoding 61 | [compileGroovy, compileTestGroovy]*.groovyOptions*.encoding = defaultEncoding 62 | 63 | task licenseCheckGroovy(type: com.hierynomus.gradle.license.tasks.LicenseCheck) { 64 | source = fileTree(dir: "src").include("**/*.groovy") 65 | } 66 | tasks.license.dependsOn licenseCheckGroovy 67 | 68 | license { 69 | header rootProject.file("license-header.txt") 70 | skipExistingHeaders true 71 | ignoreFailures false 72 | excludes (['**/*.properties', '**/*.txt', '**/hello*/']) 73 | } 74 | 75 | configurations { 76 | [apiElements, runtimeElements].each { 77 | it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) } 78 | it.outgoing.artifact(shadowJar) 79 | } 80 | } 81 | 82 | configurations { 83 | asciidoctorExt 84 | } 85 | 86 | dependencies { 87 | shadow gradleTestKit() 88 | shadow localGroovy() 89 | 90 | shadow group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' 91 | 92 | implementation "org.ow2.asm:asm:$asmVersion" 93 | implementation "org.ow2.asm:asm-commons:$asmVersion" 94 | implementation "org.ow2.asm:asm-tree:$asmVersion" 95 | implementation "org.ow2.asm:asm-util:$asmVersion" 96 | implementation "org.ow2.asm:asm-analysis:$asmVersion" 97 | 98 | testImplementation('org.spockframework:spock-core:2.3-groovy-3.0') { 99 | exclude module: 'groovy-all' 100 | } 101 | testImplementation 'cglib:cglib-nodep:3.3.0' 102 | testImplementation 'org.objenesis:objenesis:3.2' 103 | testImplementation ('com.athaydes:spock-reports:2.3.1-groovy-3.0') { transitive = false} 104 | 105 | asciidoctorExt 'com.bmuschko:asciidoctorj-tabbed-code-extension:0.3' 106 | } 107 | 108 | gradlePlugin { 109 | website = 'https://github.com/beryx/badass-runtime-plugin/' 110 | vcsUrl = 'https://github.com/beryx/badass-runtime-plugin' 111 | 112 | plugins { 113 | jlink { 114 | id = 'org.beryx.runtime' 115 | implementationClass = 'org.beryx.runtime.RuntimePlugin' 116 | displayName = 'Badass Runtime Plugin' 117 | description = 'A Gradle plugin that assembles your modules into a custom runtime image' 118 | tags.set(['runtime', 'jlink', 'jpms', 'image']) 119 | } 120 | } 121 | } 122 | 123 | shadowJar { 124 | archiveClassifier = null 125 | relocate 'org.objectweb.asm', 'org.beryx.jlink.shadow.asm' 126 | } 127 | 128 | jar { 129 | enabled = false 130 | dependsOn shadowJar 131 | 132 | manifest { 133 | attributes 'Implementation-Title': "${project.archivesBaseName}", 134 | 'Implementation-Version': badassRuntimePluginVersion 135 | } 136 | } 137 | 138 | test { 139 | useJUnitPlatform() 140 | testLogging { 141 | exceptionFormat = 'full' 142 | } 143 | } 144 | 145 | asciidoctor { 146 | configurations 'asciidoctorExt' 147 | setSourceDir file("doc") 148 | baseDirFollowsSourceDir() 149 | sources {include 'index.adoc'} 150 | logDocuments = true 151 | attributes 'source-highlighter': 'coderay', 152 | 'coderay-linenums-mode': 'table', 153 | 'project-version': version, 154 | icons: 'font', 155 | imagesdir: 'img', 156 | 'data-uri': '', 157 | linkattrs: true, 158 | linkcss: true, 159 | 'git-tag': badassRuntimePluginTag, 160 | 'blob-root': "https://github.com/beryx/badass-runtime-plugin/blob/$badassRuntimePluginTag" 161 | } 162 | 163 | gitPublish { 164 | repoUri = 'https://github.com/beryx/badass-runtime-plugin.git' 165 | branch = 'gh-pages' 166 | 167 | def pgType = project.hasProperty('ghPageType') ? ghPageType : 'latest' 168 | if(pgType == 'init') { 169 | contents .from file("ghpages") 170 | } else if(pgType == 'list') { 171 | contents.from file("build/release-list") 172 | } else { 173 | contents.from file(asciidoctor.outputDir.path) 174 | contents.from file("build/docs") 175 | } 176 | 177 | def docDir = Boolean.valueOf(badassRuntimePluginReleaseBuild) ? 'releases' : 'snapshots' 178 | if(pgType == 'init') { 179 | contents.into "." 180 | } else if(pgType == 'list') { 181 | contents.into "." 182 | preserve { 183 | include '**' 184 | exclude "releases.md" 185 | } 186 | } else if(pgType == 'version') { 187 | gitPublishPush.enabled = (docDir != 'snapshots') 188 | contents.into "$docDir/$badassRuntimePluginVersion" 189 | preserve { 190 | include '**' 191 | exclude "$docDir/$badassRuntimePluginVersion" 192 | } 193 | } else { 194 | contents.into "$docDir/latest" 195 | preserve { 196 | include '**' 197 | exclude "$docDir/latest" 198 | } 199 | } 200 | } 201 | 202 | task "update-release-list" { 203 | doLast { 204 | def docBaseUrl = 'http://badass-runtime-plugin.beryx.org' 205 | updateReleaseList(docBaseUrl) 206 | } 207 | } 208 | 209 | def updateReleaseList(String docBaseUrl) { 210 | List releases = getReleases() 211 | def markdown = getReleasesMarkdown(releases, docBaseUrl) 212 | def releaseListDir = new File('build/release-list') 213 | releaseListDir.mkdirs() 214 | new File(releaseListDir, 'releases.md').write(markdown) 215 | } 216 | 217 | def getReleases() { 218 | GitHub gitHubApi = GitHub.connectUsingPassword(System.getenv('GRGIT_USER') ?: '', '') 219 | def releases = gitHubApi.getOrganization("beryx") 220 | .getRepository("badass-runtime-plugin") 221 | .getDirectoryContent("releases", "gh-pages")*.name 222 | releases.removeAll { !it || it == 'latest' } 223 | 224 | releases.sort { o1, o2 -> 225 | if(!o1) return o2 ? 1 : 0 226 | if(!o2) return -1 227 | String[] tokens1 = o1.split("\\.") 228 | String[] tokens2 = o2.split("\\.") 229 | int len = Math.min(tokens1.length, tokens2.length) 230 | for(int i=0; i tokens1[i] 238 | } 239 | if(result != 0) return result 240 | } 241 | tokens2.length <=> tokens1.length 242 | } 243 | releases 244 | } 245 | 246 | def getReleasesMarkdown(List releases, String docBaseUrl) { 247 | def releasesTemplate = ''' 248 | ## List of all releases ## 249 | 250 | **Latest snapshot** 251 | - [documentation]($docBaseUrl/snapshots/latest) 252 | - [groovydoc]($docBaseUrl/snapshots/latest/groovydoc) 253 | 254 | <% releases.each { %>**Release $it** 255 | - [documentation]($docBaseUrl/releases/$it) 256 | - [groovydoc]($docBaseUrl/releases/$it/groovydoc) 257 | 258 | <% } %> 259 | '''.stripIndent() 260 | 261 | def engine = new SimpleTemplateEngine() 262 | def template = engine.createTemplate(releasesTemplate) 263 | def binding = [ 264 | docBaseUrl: docBaseUrl, 265 | releases : releases 266 | ] 267 | 268 | template.make(binding).toString() 269 | } 270 | -------------------------------------------------------------------------------- /doc/examples.adoc: -------------------------------------------------------------------------------- 1 | [[examples]] 2 | = Examples 3 | 4 | The following projects illustrate how to use this plugin to create custom runtime images: 5 | 6 | * https://github.com/beryx-gist/badass-runtime-example[badass-runtime-example] - a 'Hello world' application using slf4j and logback. 7 | * https://github.com/beryx-gist/badass-runtime-example-javafx[badass-runtime-example-javafx] - a 'Hello world' JavaFX application. 8 | * https://github.com/beryx-gist/badass-runtime-example-kotlin-tornadofx[badass-runtime-example-kotlin-tornadofx] - a 'Hello world' application written in Kotlin using https://github.com/edvin/tornadofx[tornadofx]. 9 | * https://github.com/beryx-gist/badass-runtime-spring-petclinic[badass-runtime-spring-petclinic] - creates a custom runtime image of the https://github.com/spring-projects/spring-petclinic[Spring PetClinic] application. 10 | * https://github.com/beryx-gist/badass-runtime-pacman[badass-runtime-pacman] - creates a custom runtime image of Pacman from the https://github.com/AlmasB/FXGLGames[FXGLGames] repository. 11 | -------------------------------------------------------------------------------- /doc/index.adoc: -------------------------------------------------------------------------------- 1 | = The Badass Runtime Plugin 2 | :author: Serban Iordache 3 | :revnumber: {project-version} 4 | :toc: left 5 | :toclevels: 3 6 | :imagesdir: img 7 | :data-uri: 8 | :caption: 9 | :last-update-label!: 10 | :linkattrs: 11 | 12 | :leveloffset: +1 13 | 14 | :toc!: 15 | 16 | include::introduction.adoc[] 17 | 18 | include::user_guide.adoc[] 19 | 20 | include::examples.adoc[] 21 | 22 | :leveloffset: -1 23 | -------------------------------------------------------------------------------- /doc/introduction.adoc: -------------------------------------------------------------------------------- 1 | [[introduction]] 2 | = Introduction 3 | 4 | The badass-runtime plugin allows you to create custom runtime images for non-modular applications. 5 | It also lets you create an application installer with the https://openjdk.java.net/jeps/392[jpackage] tool. 6 | 7 | TIP: For modular applications use the https://badass-jlink-plugin.beryx.org/releases/latest/[Badass-JLink plugin]. 8 | 9 | Creating a custom runtime image for a non-modular application involves the following steps: 10 | 11 | - create a classic (pre-Java-9-style) distribution using a task 12 | such as https://docs.gradle.org/current/userguide/application_plugin.html#sec:application_tasks[installDist] or 13 | https://imperceptiblethoughts.com/shadow/application-plugin/#distributing-the-shadow-jar[installShadowDist] 14 | - use _jlink_ to create a custom JRE containing only the modules required by the application 15 | - merge the pre-Java-9-style distribution into the custom JRE and adjust the start scripts with appropriate 16 | JVM settings flags such as `--module-path`. 17 | 18 | The plugin provides a `runtime` task that automates the above steps 19 | and a `runtimeZip` task that additionally creates a zip archive of the custom runtime image. 20 | In addition, a `suggestModules` task helps you choose the modules to include in your custom JRE. 21 | With the `jpackage` task you can create a platform-specific installer for your application. 22 | 23 | WARNING: The plugin requires Java 11 and Gradle 7.0 or newer. 24 | While it might work with some combinations of older Java and Gradle versions, these are not officially supported. 25 | If you are forced to work with an older Gradle release, you should use https://badass-runtime-plugin.beryx.org/releases/1.12.7/[the version 1.12.7] of this plugin. 26 | 27 | To use the plugin, include the following in your build script: 28 | 29 | [source,groovy] 30 | [subs="attributes",options="nowrap"] 31 | ---- 32 | plugins { 33 | id 'org.beryx.runtime' version '{project-version}' 34 | } 35 | ---- 36 | 37 | Applying this plugin also implicitly applies the https://docs.gradle.org/current/userguide/application_plugin.html[Application plugin]. 38 | 39 | The badass-runtime plugin adds an extension named `runtime`. 40 | The sample below shows a few configuration options. 41 | 42 | [source,groovy] 43 | ---- 44 | runtime { 45 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] 46 | modules = ['java.naming', 'java.xml'] 47 | } 48 | ---- 49 | 50 | The next sections provide detailed information on how to configure the plugin. 51 | 52 | NOTE: The source code is available on https://github.com/beryx/badass-runtime-plugin[GitHub] and is licensed under the {blob-root}/LICENSE[Apache-2.0 license]. 53 | -------------------------------------------------------------------------------- /doc/user_guide.adoc: -------------------------------------------------------------------------------- 1 | [[user_guide]] 2 | = User Guide 3 | 4 | This plugin works with non-modular applications, that is, applications that run on the class-path and 5 | don't make use of module descriptors. 6 | Nevertheless, in order to use this plugin, your non-modular application must be compatible with the Java version 7 | of your target JRE (11 or newer). 8 | 9 | 10 | == Tasks 11 | runtime:: Creates an image containing your application, a custom JRE, and appropriate start scripts. + 12 | If the property `distDir` is not set, this task depends on either `installDist` or 13 | `installShadowDist` (if the https://github.com/johnrengelman/shadow[Shadow plugin] is applied). 14 | 15 | 16 | runtimeZip:: Creates a zip archive of the custom runtime image. + 17 | _depends on_: `runtime` 18 | 19 | suggestModules:: Displays a list of modules that are probably required by your application. 20 | This list will be used when creating the custom JRE if you don't configure the `modules` property 21 | explicitly within the `runtime` extension. + 22 | Setting the `modules` property explicitly is strongly recommended. 23 | The list of modules suggested by this task is a good value to start with, but it may miss some 24 | required modules or include unnecessary ones, so you may need to adjust it. + 25 | _depends on_: `jar` 26 | 27 | jpackageImage:: Uses the https://openjdk.java.net/jeps/392/[jpackage] tool to create a platform-specific application image. + 28 | _depends on_: `runtime` + 29 | 30 | jpackage:: Uses the https://openjdk.java.net/jeps/392/[jpackage] tool to create a platform-specific application installer. + 31 | _depends on_: `jpackageImage` + 32 | 33 | 34 | == Properties 35 | 36 | distDir:: The directory containing the application pre-Java-9-style distribution. + 37 | You rarely need to set this property explicitly, because the plugin automatically 38 | initializes it with the directory in which the `installDist` or the `installShadowDist` task 39 | (if the https://github.com/johnrengelman/shadow[Shadow plugin] is applied) creates the distribution. + 40 | _usage example_: `distDir = file("$buildDir/install/myapp")` 41 | 42 | imageDir:: The directory into which the custom runtime image should be generated. + 43 | (If you use the `targetPlatform` method to generate images for other platforms, the corresponding images will be created in subdirectories of `imageDir`.) + 44 | _defaultValue_: `_buildDir_/image` + 45 | _usage example_: `imageDir = file("$buildDir/myapp-image")` 46 | 47 | imageZip:: The file into which a zip archive of the custom runtime image should be created. + 48 | _defaultValue_: `_buildDir_/image.zip"` + 49 | _usage example_: `imageZip = file("$buildDir/myapp-image.zip")` 50 | 51 | options:: A list of options to be passed to _jlink_ when creating the custom JRE. + 52 | _defaultValue_: empty list + 53 | _usage example_: `options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']` 54 | 55 | [#javaHome] 56 | javaHome:: The path to the JDK providing the tools needed by the plugin (_javac_, _jar_, _jlink_ etc.). + 57 | _defaultValue_: the first non-empty value from: + 58 | pass:[    ] - the `badass.runtime.java.home` system property + 59 | pass:[    ] - the `BADASS_RUNTIME_JAVA_HOME` environment variable + 60 | pass:[    ] - the https://docs.gradle.org/current/userguide/toolchains.html[Java toolchain] configured in the Gradle script + 61 | pass:[    ] - the `java.home` system property (only if it points to a JRE containing the `javac`, `jar`, and `jlink` tools) + 62 | pass:[    ] - the `JAVA_HOME` environment variable + 63 | _usage example_: `javaHome = '/usr/lib/jvm/open-jdk'` 64 | 65 | modules:: The list of modules to be included in the custom JRE. + 66 | _defaultValue_: empty list (the modules provided by the `suggestModules` task will be used) + 67 | _usage example_: `modules = ['java.naming', 'java.xml']` 68 | 69 | additive:: In many cases, the list of modules provided by the `suggestModules` task contains exactly the modules required by your application. 70 | This means that you don’t need to configure the `modules` property, because the plugin can figure out by itself which modules are required. 71 | In some other cases, the "suggested" list of modules is _almost_ right, in the sense that it only misses one or a few modules. 72 | In these cases, you are allowed to specify only the missing modules in the `modules` property, 73 | and you instruct the plugin to add them to the list of suggested modules, by setting the property `additive` to true. + 74 | _defaultValue_: false + 75 | _usage example_: `additive = true` 76 | 77 | == Methods 78 | 79 | [maroon]##addOptions##(String... [purple]##options##):: Adds options to be passed to _jlink_. 80 | It is an alternative way of setting the `options` property. 81 | You can call this method multiple times. + 82 | _usage example_: `addOptions '--no-header-files', '--no-man-pages'` 83 | 84 | [maroon]##targetPlatform##(String [purple]##name##, String [purple]##jdkHome##, List [purple]##options## = []):: Instructs the plugin to use _jlink_ to generate an application image for a specific platform. + 85 | [red]##**This method is not for configuring the installable packages produced by jpackage.**## <> + 86 | By default, the plugin generates an image for the platform it runs on. 87 | To create images for other platforms, you need to call the `targetPlatform` method (one call per target platform). + 88 | [purple]##**name**##: an identifier of your choice that will be appended to the `imageDir` and `imageZip` properties to 89 | determine the location of the image directory and of the image archive. + 90 | [purple]##**jdkHome**##: the path to the target platform JDK. + 91 | [purple]##**options**##: an optional list of platform-specific options. 92 | These options will pe passed to _jlink_ in addition to those provided by the `options` property of the `runtime` extension. + 93 | [purple]##_NOTE_##: This is only a convenience method. There is a more powerful `targetPlatform` method (described below), which allows configuring additional parameters of the target platform. 94 | 95 | [cols="1,100", frame=none, grid=none] 96 | |=== 97 | a| a| .Usage example 98 | [source,groovy] 99 | ---- 100 | runtime { 101 | ... 102 | targetPlatform('linux-x64', '/usr/lib/jvm/jdk_x64_linux_hotspot_11_28') 103 | targetPlatform('linux-s390x', '/usr/lib/jvm/jdk_s390x_linux_hotspot_11_28', 104 | ['--endian', 'big']) 105 | ... 106 | } 107 | ---- 108 | 109 | For a project named `hello`, executing the `runtimeZip` task with the above configuration, and assuming default values for the other properties, 110 | the plugin will generate the platform-specific images in the directories 111 | `build/image/hello-linux-x64` and `build/image/hello-linux-s390x`. 112 | The archived images will be available in `build/image-linux-x64.zip` and `build/image-linux-s390x.zip`. 113 | |=== 114 | 115 | [maroon]##targetPlatform##(String [purple]##name##, Action [purple]##action##):: This more powerful version of the `targetPlatform` method allows configuring the target platform parameters using a script block. + 116 | [purple]##**name**##: an identifier of your choice that will be appended to the `imageDir` and `imageZip` properties to 117 | determine the location of the image directory and of the image archive. + 118 | [purple]##**action**##: a script block for configuring the target platform parameters. + 119 |      _Parameters:_ + 120 |          [purple]##**jdkHome**##: the path to the target platform JDK. + 121 |          [purple]##**options**##: an optional list of platform-specific options. + 122 |      _Methods:_ + 123 |          [maroon]##addOptions##(String... [purple]##options##): an alternative way of setting the `options` property. + 124 |          [maroon]##jdkDownload##(String [purple]##downloadUrl##, Closure [purple]##downloadConfig##=null): helper method for setting [purple]##jdkHome##. + 125 |              It downloads and unpacks a JDK distribution from the given URL. + 126 |              The optional closure allows configuring the following parameters: + 127 |                - [purple]##downloadDir##: the directory in which the distribution is downloaded and unpacked. + 128 |                    _defaultValue_: `_buildDir_/jdks/_targetPlatform-name_` + 129 |                - [purple]##archiveName##: the name under which the archived distribution should be saved. + 130 |                    _defaultValue_: `jdk` + 131 |                - [purple]##archiveExtension##: accepted values: `tar.gz` and `zip`. + 132 |                    _defaultValue_: `null` (inferred from the URL) + 133 |                - [purple]##pathToHome##: the relative path to the JDK home in the unpacked distribution. + 134 |                    _defaultValue_: `null` (inferred by scanning the unpacked distribution) + 135 |                - [purple]##overwrite##: if `true`, the plugin overwrites an already existing distribution. + 136 |                    _defaultValue_: `false` 137 | 138 | [cols="1,100", frame=none, grid=none] 139 | |=== 140 | a| a| .Usage example 141 | [source,groovy] 142 | ---- 143 | runtime { 144 | ... 145 | targetPlatform("linux-s390x") { 146 | jdkHome = "/usr/lib/jvm/linux-s390x/jdk-14.0.1_7" 147 | addOptions("--endian", "big") 148 | } 149 | 150 | targetPlatform("win") { 151 | jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7.1/OpenJDK14U-jdk_x64_windows_hotspot_14.0.1_7.zip") 152 | } 153 | 154 | targetPlatform("mac") { 155 | jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7/OpenJDK14U-jdk_x64_mac_hotspot_14.0.1_7.tar.gz") { 156 | downloadDir = "$buildDir/myMac" 157 | archiveName = "my-mac-jdk" 158 | archiveExtension = "tar.gz" 159 | pathToHome = "jdk-14.0.1+7/Contents/Home" 160 | overwrite = true 161 | } 162 | } 163 | ... 164 | } 165 | ---- 166 | |=== 167 | 168 | [maroon]##enableCds##(Action [purple]##action## = null):: [red]##Experimental - requires Java 13 or newer## + 169 | Enables Class Data Sharing (CDS). + 170 | [purple]##**action**##: an optional script block for configuring the class data sharing. + 171 |      _Parameters:_ + 172 |          [purple]##**sharedArchiveFile**##: the path and name of the class data sharing archive file. + 173 |              It supports the Mustache syntax and placeholders described in the <> section. + 174 |              _defaultValue:_ + 175 |                  `lib/server/.jsa` on Unix-like systems + 176 |                  `bin\server\.jsa` on Windows 177 | 178 | 179 | 180 | [cols="1,100", frame=none, grid=none] 181 | |=== 182 | a| a| .Usage example 183 | [source,groovy] 184 | ---- 185 | runtime { 186 | ... 187 | enableCds() 188 | } 189 | ---- 190 | 191 |      or 192 | 193 | [source,groovy] 194 | ---- 195 | runtime { 196 | ... 197 | enableCds { 198 | sharedArchiveFile = "{{HOME_DIR}}/${applicationName}.jsa" 199 | } 200 | } 201 | ---- 202 | 203 | When the `enableCds` method is used, the plugin creates a base CDS archive of the image by executing 204 | `$imageDir/bin/java -Xshare:dump`. This means that you cannot use `enableCds` when targeting another platform. 205 | 206 | The plugin also configures a dynamic AppCDS archive in the start scripts. 207 | If no file is found at the `sharedArchiveFile` location, the application is started with the `-XX:ArchiveClassesAtExit` option, 208 | which will create a dynamic AppCDS archive at this location. 209 | Otherwise, the application is started with the `-XX:SharedArchiveFile` option and uses the existing AppCDS archive. 210 | 211 | [purple]##_NOTE_##: Start scripts are not included in the installable packages generated by `jpackage`. 212 | As a result, only the base CDS archive of the image is used by the packaged application. 213 | 214 | 215 | |=== 216 | 217 | [[scriptBlocks]] 218 | == Script blocks 219 | 220 | The `runtime` extension can also contain the script blocks detailed below. 221 | 222 | [#launcher] 223 | === launcher 224 | 225 | The plugin generates script files for launching your application. 226 | These script files can be customized by configuring the `launcher` block. 227 | 228 | Environment variables can be included by using the https://en.wikipedia.org/wiki/Mustache_(template_system)[Mustache syntax], 229 | that is, by enclosing their name between `{{` and `}}`. 230 | Additionally, you can use the following placeholders: 231 | 232 | - `{{BIN_DIR}}` - the _bin_ directory of the custom runtime image 233 | - `{{HOME_DIR}}` - user's home directory (`$HOME` on Unix-like systems, `%USERPROFILE%` on Windows) 234 | 235 | 236 | jvmArgs:: list of JVM arguments to be passed to the java executable. + 237 | _defaultValue_: the arguments configured in the `applicationDefaultJvmArgs` property of the `application` extension 238 | 239 | noConsole:: This boolean property has an effect only on Windows. It is ignored on other platforms. + 240 | If true, the application will be launched without an associated console window (using `javaw` instead of `java`). + 241 | _defaultValue_: false 242 | 243 | runInBinDir:: If true, the start script will `cd` in the `bin` directory of the image before executing the application. + 244 | _defaultValue_: false 245 | 246 | unixScriptTemplate:: the template for generating the script file for Unix-like systems. + 247 | _defaultValue_: null (the plugin uses its own template) 248 | 249 | windowsScriptTemplate:: the template for generating the script file for Windows-based systems. + 250 | _defaultValue_: null (the plugin uses its own template) 251 | 252 | The plugin uses http://docs.groovy-lang.org/latest/html/api/groovy/text/SimpleTemplateEngine.html[Groovy's SimpleTemplateEngine] 253 | to parse the templates, with the following variables available: 254 | 255 | - applicationName 256 | - mainClassName 257 | 258 | _Usage example_ 259 | [source,groovy,indent=0,subs="verbatim,attributes",role="primary"] 260 | .Groovy 261 | ---- 262 | runtime { 263 | ... 264 | launcher { 265 | jvmArgs = [ 266 | '-Dlog4j.debug=true', '-Dlog4j.configurationFile={{BIN_DIR}}/log4j2.xml', 267 | '-DdbHost', '{{PGHOST}}' 268 | ] 269 | unixScriptTemplate = file('unixStartScript.txt') 270 | windowsScriptTemplate = file('windowsStartScript.txt') 271 | } 272 | ... 273 | } 274 | ---- 275 | 276 | [source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"] 277 | .Kotlin 278 | ---- 279 | runtime { 280 | ... 281 | launcher { 282 | jvmArgs = listOf( 283 | "-Dlog4j.debug=true", "-Dlog4j.configurationFile={{BIN_DIR}}/log4j2.xml", 284 | "-DdbHost", "{{PGHOST}}" 285 | ) 286 | unixScriptTemplate = file("unixStartScript.txt") 287 | windowsScriptTemplate = file("windowsStartScript.txt") 288 | } 289 | ... 290 | } 291 | ---- 292 | 293 | 294 | === jpackage 295 | 296 | This script block allows you to customize the https://openjdk.java.net/jeps/392/[jpackage]-based generation of platform-specific installable packages. 297 | 298 | [#jpackageHome] 299 | jpackageHome:: The path to the JDK providing the jpackage tool. + 300 | _defaultValue_: the first non-empty value from: + 301 | pass:[    ] - the `badass.runtime.jpackage.home` system property + 302 | pass:[    ] - the `BADASS_RUNTIME_JPACKAGE_HOME` environment variable + 303 | pass:[    ] - the https://docs.gradle.org/current/userguide/toolchains.html[Java toolchain] configured in the Gradle script + 304 | pass:[    ] - the `java.home` system property (only if it points to a JRE containing the `jpackage` tool) + 305 | pass:[    ] - the `JAVA_HOME` environment variable + 306 | _usage example_: `jpackageHome = "/usr/lib/jvm/jdk16"` 307 | 308 | outputDir:: Convenience property for setting both `imageOutputDir` and 309 | `installerOutputDir` with the value _buildDir_/_outputDir_. + 310 | _defaultValue_: `"jpackage"` + 311 | _usage example_: `outputDir = "my-packaging"` 312 | 313 | 314 | imageOutputDir:: the directory passed as argument to the `--output` option when running `jpackage` to create an application image. + 315 | _defaultValue_: `_buildDir_/_outputDir_` + 316 | _usage example_: `imageOutputDir = file("$buildDir/my-packaging-image")` 317 | 318 | imageName:: the argument passed to the `--name` option when running `jpackage` to create an application image. + 319 | _defaultValue_: `_project.name_` + 320 | _usage example_: `imageName = "MyApp"` 321 | 322 | imageOptions:: list of additional options to be passed to the `jpackage` executable when creating the appliction image. + 323 | _defaultValue_: empty list + 324 | _usage example_: `imageOptions = ["--win-console"]` 325 | 326 | resourceDir:: the directory passed as argument to the `--resource-dir` option when running `jpackage` to create an application installer. 327 | It is also applicable when creating an application image when you want your own application image instead of the default java image. + 328 | _usage example_: `resourceDir = file("$buildDir/my-packaging-resources")` 329 | 330 | skipInstaller:: boolean value that lets you generate only the platform-specific application image and skip the generation of the platform-specific application installer. + 331 | _defaultValue_: false + 332 | _usage example_: `skipInstaller = true` 333 | 334 | installerType:: the type of installer to be generated. + 335 | _defaultValue_: null (all supported types for the current platform will be generated) + 336 | _usage example_: `installerType = "rpm"` 337 | 338 | installerOutputDir:: the directory passed as argument to the `--output` option when running `jpackage` to create an application installer. + 339 | _defaultValue_: `_buildDir_/_outputDir_` + 340 | _usage example_: `installerOutputDir = file("$buildDir/my-packaging-installer")` 341 | 342 | installerName:: the argument passed to the `--name` option when running `jpackage` to create an application installer. + 343 | _defaultValue_: `_project.name_` + 344 | _usage example_: `installerName = "MyApp"` 345 | 346 | jvmArgs:: list of JVM arguments to be passed to the virtual machine. + 347 | _defaultValue_: the `jvmArgs` value configured in the `launcher` block, or the arguments configured in the `applicationDefaultJvmArgs` property of the `application` extension. + 348 | [purple]##_NOTE_##: If the default value is used, and it contains the placeholder `{{BIN_DIR}}`, the plugin replaces this placeholder with `$APPDIR/..` when passing the arguments to `jpackage`. 349 | This is the correct approach in most cases. If it doesn't work in your case, you need to explicitly configure *jvmArgs* in the `jpackage` block. + 350 | Currently, jpackage doesn't support environment variables in `--java-options`. Therefore, you cannot use environment variable names enclosed between `{{` and `}}` in `jvmArgs`. 351 | 352 | args:: list of arguments to be passed to the application. + 353 | _defaultValue_: the arguments configured in the `args` property of the `run` task 354 | 355 | appVersion:: the argument passed to the `--app-version` option when running `jpackage` when executing the `jpackage` and `jpackageImage` tasks. + 356 | _defaultValue_: the project version + 357 | _usage example_: `appVersion = "1.0.0"` 358 | 359 | installerOptions:: list of additional options to be passed to the `jpackage` executable when creating the application installer. + 360 | _defaultValue_: empty list + 361 | _usage example_: `installerOptions = ["--win-console"]` 362 | 363 | targetPlatformName:: This property is required only when using the `targetPlatform` method. 364 | It specifies which of the images produced by jlink should be used as runtime image by jpackage. 365 | Its value must match the name provided in one of the calls to the `targetPlatform` method. + 366 | _defaultValue_: null + 367 | _usage example_: `targetPlatformName = "linux"` 368 | 369 | [#jpackageWarning] 370 | WARNING: [red]##**In contrast to jlink, _jpackage_ is not able to produce installers for other platforms.**## 371 | For example, to create an installer for Linux, you must run jpackage on a Linux machine. 372 | You cannot do it on a Windows or Mac platform. 373 | 374 | TIP: If you need to create installers for more than one platform, it's probably better not to use _targetPlatform_. 375 | Instead, you run the same build on different machines. 376 | If your project is on GitHub, you can automate this by using GitHub Actions, as seen in https://github.com/beryx-gist/badass-runtime-example-javafx[this example]. 377 | 378 | 379 | mainJar:: the argument passed to the `--main-jar` option when running `jpackage` to create an application image. + 380 | Usually, you don't need to set this property, unless you also explicitly set `distDir`. + 381 | _defaultValue_: the name of the JAR file produced by the `installDist` or the `installShadowDist` task + 382 | _usage example_: `mainJar = "my-app-1.0.1.jar"` 383 | 384 | mainClass:: the argument passed to the `--main-class` option when running `jpackage` to create an application image. + 385 | Usually, you don't need to set this property, unless you also explicitly set `distDir`. + 386 | _defaultValue_: the `mainClass` configured for the `application` plugin + 387 | _usage example_: `mainClass = "org.example.hello.Greeter"` 388 | 389 | 390 | _Usage example_ 391 | [source,groovy,indent=0,subs="verbatim,attributes",role="primary"] 392 | .Groovy 393 | ---- 394 | runtime { 395 | ... 396 | jpackage { 397 | jpackageHome = '/usr/lib/jvm/jdk16' 398 | outputDir = 'my-packaging' 399 | // imageOutputDir = file("$buildDir/my-packaging-image") 400 | // installerOutputDir = file("$buildDir/my-packaging-installer") 401 | imageName = 'MyApp' 402 | imageOptions = ['--win-console'] 403 | skipInstaller = false 404 | installerName = 'MyApp' 405 | installerType = 'msi' 406 | installerOptions = ['--win-menu', '--win-shortcut'] 407 | } 408 | ... 409 | } 410 | ---- 411 | 412 | [source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"] 413 | .Kotlin 414 | ---- 415 | runtime { 416 | ... 417 | jpackage { 418 | jpackageHome = "/usr/lib/jvm/jdk16" 419 | outputDir = "my-packaging" 420 | // imageOutputDir = file("$buildDir/my-packaging-image") 421 | // installerOutputDir = file("$buildDir/my-packaging-installer") 422 | imageName = "MyApp" 423 | imageOptions = listOf("--win-console") 424 | skipInstaller = false 425 | installerName = "MyApp" 426 | installerType = "msi" 427 | installerOptions = listOf("--win-menu", "--win-shortcut") 428 | } 429 | ... 430 | } 431 | ---- 432 | 433 | 434 | 435 | -------------------------------------------------------------------------------- /ghpages/CNAME: -------------------------------------------------------------------------------- 1 | badass-runtime-plugin.beryx.org 2 | -------------------------------------------------------------------------------- /ghpages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ghpages/releases/latest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

No release yet

5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | badassRuntimePluginVersionMajor = 1 2 | badassRuntimePluginVersionMinor = 13 3 | badassRuntimePluginVersionPatch = 2 4 | # badassRuntimePluginVersionLabel = rc-1 5 | badassRuntimePluginReleaseBuild = false 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | if [ "${GITHUB_REF}" == "refs/heads/master" ]; then 5 | if [ "$(git ls-remote origin gh-pages)" == "" ]; then 6 | echo Start gitPublishPush with ghPageType=init 7 | ./gradlew --no-daemon -i -s gitPublishPush --rerun-tasks -PghPageType=init 8 | echo Finished gitPublishPush with ghPageType=init 9 | fi 10 | echo Start gitPublishPush with ghPageType=latest 11 | ./gradlew --no-daemon -i -s gitPublishPush --rerun-tasks -PghPageType=latest 12 | echo Finished gitPublishPush with ghPageType=version 13 | 14 | echo Start gitPublishPush with ghPageType=version 15 | ./gradlew --no-daemon -i -s gitPublishPush --rerun-tasks -PghPageType=version 16 | echo Finished gitPublishPush with ghPageType=version 17 | 18 | echo Start updating releases.md 19 | ./gradlew --no-daemon -i -s update-release-list gitPublishPush --rerun-tasks -PghPageType=list 20 | echo Finished updating releases.md 21 | fi 22 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.gradle.enterprise" version "3.0" 3 | } 4 | 5 | gradleEnterprise { 6 | buildScan { 7 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 8 | termsOfServiceAgree = 'yes' 9 | } 10 | } 11 | 12 | rootProject.name = 'badass-runtime-plugin' 13 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/BaseTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.RuntimePluginExtension 20 | import org.beryx.runtime.util.Util 21 | import org.gradle.api.DefaultTask 22 | import org.gradle.api.tasks.Internal 23 | 24 | import static org.beryx.runtime.util.Util.EXEC_EXTENSION 25 | 26 | @CompileStatic 27 | class BaseTask extends DefaultTask { 28 | @Internal 29 | final RuntimePluginExtension extension 30 | 31 | BaseTask() { 32 | this.extension = (RuntimePluginExtension)project.extensions.getByName(RuntimePlugin.EXTENSION_NAME) 33 | group = 'build' 34 | } 35 | 36 | @Internal 37 | String getJavaHomeOrDefault() { 38 | return extension.javaHome.present ? extension.javaHome.get() : defaultJavaHome 39 | } 40 | 41 | @Internal 42 | String getDefaultJavaHome() { 43 | def value = System.properties['badass.runtime.java.home'] 44 | if(value) return value 45 | value = System.getenv('BADASS_RUNTIME_JAVA_HOME') 46 | if(value) return value 47 | value = Util.getDefaultToolchainJavaHome(project) 48 | if(value) return value 49 | value = System.properties['java.home'] 50 | if(['javac', 'jar', 'jlink'].every { new File("$value/bin/$it$EXEC_EXTENSION").file }) return value 51 | return System.getenv('JAVA_HOME') 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/JPackageImageTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileDynamic 19 | import groovy.transform.CompileStatic 20 | import org.beryx.runtime.data.JPackageData 21 | import org.beryx.runtime.data.JPackageTaskData 22 | import org.beryx.runtime.impl.JPackageImageTaskImpl 23 | import org.gradle.api.file.Directory 24 | import org.gradle.api.logging.Logger 25 | import org.gradle.api.logging.Logging 26 | import org.gradle.api.tasks.InputDirectory 27 | import org.gradle.api.tasks.Internal 28 | import org.gradle.api.tasks.Nested 29 | import org.gradle.api.tasks.Sync 30 | import org.gradle.api.tasks.TaskAction 31 | 32 | @CompileStatic 33 | class JPackageImageTask extends BaseTask { 34 | private static final Logger LOGGER = Logging.getLogger(JPackageImageTask.class) 35 | 36 | @InputDirectory 37 | Directory getDistDir() { 38 | extension.distDir.getOrNull() ?: project.layout.buildDirectory.dir(distTask.destinationDir.path).get() 39 | } 40 | 41 | @InputDirectory 42 | Directory getJreDir() { 43 | extension.jreDir.get() 44 | } 45 | 46 | @Nested 47 | JPackageData getJpackageData() { 48 | extension.jpackageData.get() 49 | } 50 | 51 | @Internal 52 | Sync getDistTask() { 53 | (Sync)(project.tasks.findByName('installShadowDist') ?: project.tasks.getByName('installDist')) 54 | } 55 | 56 | @CompileDynamic 57 | JPackageImageTask() { 58 | description = 'Creates an application image using the jpackage tool' 59 | dependsOn(RuntimePlugin.TASK_NAME_JRE) 60 | project.afterEvaluate { 61 | dependsOn(distTask) 62 | } 63 | } 64 | 65 | @TaskAction 66 | void jpackageTaskAction() { 67 | def taskData = new JPackageTaskData() 68 | taskData.distDir = distDir.asFile 69 | taskData.jpackageData = jpackageData 70 | taskData.javaHome = javaHomeOrDefault 71 | 72 | def jreTask = (JreTask) project.tasks.getByName(RuntimePlugin.TASK_NAME_JRE) 73 | taskData.configureRuntimeImageDir(jreTask) 74 | 75 | def taskImpl = new JPackageImageTaskImpl(project, taskData) 76 | taskImpl.execute() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/JPackageTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import org.beryx.runtime.data.JPackageData 19 | import org.beryx.runtime.data.JPackageTaskData 20 | import org.beryx.runtime.impl.JPackageTaskImpl 21 | import org.gradle.api.logging.Logger 22 | import org.gradle.api.logging.Logging 23 | import org.gradle.api.tasks.Nested 24 | import org.gradle.api.tasks.TaskAction 25 | 26 | import groovy.transform.CompileStatic 27 | 28 | @CompileStatic 29 | class JPackageTask extends BaseTask { 30 | private static final Logger LOGGER = Logging.getLogger(JPackageTask.class) 31 | 32 | @Nested 33 | JPackageData getJpackageData() { 34 | extension.jpackageData.get() 35 | } 36 | 37 | JPackageTask() { 38 | description = 'Creates an application installer using the jpackage tool' 39 | dependsOn(RuntimePlugin.TASK_NAME_JPACKAGE_IMAGE) 40 | } 41 | 42 | @TaskAction 43 | void jpackageTaskAction() { 44 | def taskData = new JPackageTaskData() 45 | taskData.jpackageData = jpackageData 46 | taskData.javaHome = javaHomeOrDefault 47 | taskData.configureAppImageDir() 48 | 49 | def taskImpl = new JPackageTaskImpl(project, taskData) 50 | taskImpl.execute() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/JreTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.JreTaskData 20 | import org.beryx.runtime.data.TargetPlatform 21 | import org.beryx.runtime.impl.JreTaskImpl 22 | import org.gradle.api.file.Directory 23 | import org.gradle.api.provider.MapProperty 24 | import org.gradle.api.tasks.Input 25 | import org.gradle.api.tasks.Nested 26 | import org.gradle.api.tasks.OutputDirectory 27 | import org.gradle.api.tasks.TaskAction 28 | 29 | @CompileStatic 30 | class JreTask extends BaseTask { 31 | @Input 32 | List getOptions() { 33 | extension.options.get() 34 | } 35 | 36 | @Input 37 | boolean isAdditive() { 38 | extension.additive.get() 39 | } 40 | 41 | @Input 42 | List getModules() { 43 | extension.modules.get() 44 | } 45 | 46 | @Input 47 | String getJavaHome() { 48 | javaHomeOrDefault 49 | } 50 | 51 | @Nested 52 | MapProperty getTargetPlatforms() { 53 | extension.targetPlatforms 54 | } 55 | 56 | @OutputDirectory 57 | Directory getJreDir() { 58 | extension.jreDir.get() 59 | } 60 | 61 | @OutputDirectory 62 | File getJreDirAsFile() { 63 | jreDir.asFile 64 | } 65 | 66 | JreTask() { 67 | dependsOn('jar') 68 | description = 'Creates a custom java runtime image with jlink' 69 | dependsOn(project.configurations['runtimeClasspath'].allDependencies) 70 | } 71 | 72 | @TaskAction 73 | void runtimeTaskAction() { 74 | def taskData = new JreTaskData() 75 | taskData.jreDir = jreDir.asFile 76 | taskData.options = options 77 | taskData.additive = additive 78 | taskData.modules = modules 79 | taskData.javaHome = javaHome 80 | taskData.targetPlatforms = targetPlatforms.get() 81 | 82 | def taskImpl = new JreTaskImpl(project, taskData) 83 | taskImpl.execute() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/RuntimePlugin.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.RuntimePluginExtension 20 | import org.gradle.api.GradleException 21 | import org.gradle.api.Plugin 22 | import org.gradle.api.Project 23 | import org.gradle.util.GradleVersion 24 | 25 | class RuntimePlugin implements Plugin { 26 | final static String EXTENSION_NAME = 'runtime' 27 | final static String TASK_NAME_JRE = 'jre' 28 | final static String TASK_NAME_RUNTIME = 'runtime' 29 | final static String TASK_NAME_RUNTIME_ZIP = 'runtimeZip' 30 | final static String TASK_NAME_SUGGEST_MODULES = 'suggestModules' 31 | final static String TASK_NAME_JPACKAGE_IMAGE = 'jpackageImage' 32 | final static String TASK_NAME_JPACKAGE = 'jpackage' 33 | 34 | @CompileStatic 35 | @Override 36 | void apply(Project project) { 37 | if(GradleVersion.current() < GradleVersion.version('7.0')) { 38 | throw new GradleException("This plugin requires Gradle 7.0 or newer. Try org.beryx.runtime 1.12.7 if you must use an older version of Gradle.") 39 | } 40 | project.getPluginManager().apply('application'); 41 | if(hasModuleInfo(project)) { 42 | throw new GradleException("This plugin works only with non-modular applications.\n" + 43 | "For modular applications use https://github.com/beryx/badass-jlink-plugin/.") 44 | } 45 | project.extensions.create(EXTENSION_NAME, RuntimePluginExtension, project) 46 | project.tasks.create(TASK_NAME_JRE, JreTask) 47 | project.tasks.create(TASK_NAME_RUNTIME, RuntimeTask) 48 | project.tasks.create(TASK_NAME_RUNTIME_ZIP, RuntimeZipTask) 49 | project.tasks.create(TASK_NAME_SUGGEST_MODULES, SuggestModulesTask) 50 | project.tasks.create(TASK_NAME_JPACKAGE_IMAGE, JPackageImageTask) 51 | project.tasks.create(TASK_NAME_JPACKAGE, JPackageTask) 52 | } 53 | 54 | static boolean hasModuleInfo(Project project) { 55 | Set srcDirs = project.sourceSets.main?.java?.srcDirs 56 | srcDirs?.any { it.list()?.contains('module-info.java')} 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/RuntimeTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileDynamic 19 | import groovy.transform.CompileStatic 20 | import org.beryx.runtime.data.CdsData 21 | import org.beryx.runtime.data.LauncherData 22 | import org.beryx.runtime.data.RuntimeTaskData 23 | import org.beryx.runtime.data.TargetPlatform 24 | import org.beryx.runtime.impl.RuntimeTaskImpl 25 | import org.beryx.runtime.util.Util 26 | import org.gradle.api.execution.TaskExecutionGraph 27 | import org.gradle.api.file.Directory 28 | import org.gradle.api.provider.MapProperty 29 | import org.gradle.api.tasks.* 30 | import org.gradle.api.tasks.application.CreateStartScripts 31 | import org.gradle.jvm.application.scripts.ScriptGenerator 32 | import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator 33 | 34 | @CompileStatic 35 | class RuntimeTask extends BaseTask { 36 | @Nested 37 | MapProperty getTargetPlatforms() { 38 | extension.targetPlatforms 39 | } 40 | 41 | @Nested 42 | LauncherData getLauncherData() { 43 | extension.launcherData.get() 44 | } 45 | 46 | @Nested 47 | CdsData getCdsData() { 48 | extension.cdsData.get() 49 | } 50 | 51 | @InputDirectory 52 | Directory getDistDir() { 53 | extension.distDir.getOrNull() ?: project.layout.buildDirectory.dir(distTask.destinationDir.path).get() 54 | } 55 | 56 | @OutputDirectory 57 | Directory getJreDir() { 58 | extension.jreDir.get() 59 | } 60 | 61 | @Internal 62 | Directory getImageDir() { 63 | extension.imageDir.get() 64 | } 65 | 66 | @Internal 67 | Sync getDistTask() { 68 | (Sync)(project.tasks.findByName('installShadowDist') ?: project.tasks.getByName('installDist')) 69 | } 70 | 71 | @CompileDynamic 72 | RuntimeTask() { 73 | description = 'Creates a runtime image of your application' 74 | dependsOn(RuntimePlugin.TASK_NAME_JRE) 75 | project.afterEvaluate { 76 | dependsOn(distTask) 77 | } 78 | project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph -> 79 | configureStartScripts(taskGraph.hasTask(this)) 80 | } 81 | } 82 | 83 | @OutputDirectory 84 | File getImageDirAsFile() { 85 | imageDir.asFile 86 | } 87 | 88 | void configureStartScripts(boolean asRuntimeImage) { 89 | project.tasks.withType(CreateStartScripts) { CreateStartScripts startScriptTask -> 90 | startScriptTask.mainClass.set(Util.getMainClass(project)); 91 | startScriptTask.defaultJvmOpts = launcherData.jvmArgsOrDefault 92 | startScriptTask.doLast { 93 | startScriptTask.unixScript.text = startScriptTask.unixScript.text.replace('{{BIN_DIR}}', '$APP_HOME/bin') 94 | startScriptTask.unixScript.text = startScriptTask.unixScript.text.replace('{{HOME_DIR}}', '$HOME') 95 | startScriptTask.unixScript.text = startScriptTask.unixScript.text.replaceAll(/\{\{([\w.]+)}}/, '\\$$1') 96 | 97 | startScriptTask.windowsScript.text = startScriptTask.windowsScript.text.replace('{{BIN_DIR}}', '%APP_HOME%\\\\bin') 98 | startScriptTask.windowsScript.text = startScriptTask.windowsScript.text.replace('{{HOME_DIR}}', '%USERPROFILE%') 99 | startScriptTask.windowsScript.text = startScriptTask.windowsScript.text.replaceAll(/\{\{([\w.]+)}}/, '%$1%') 100 | } 101 | startScriptTask.inputs.property('asRuntimeImage', asRuntimeImage) 102 | if(asRuntimeImage) { 103 | if(launcherData.runInBinDir) { 104 | System.properties['BADASS_RUN_IN_BIN_DIR'] = 'true' 105 | } 106 | configureCds() 107 | configureTemplate(startScriptTask.unixStartScriptGenerator, launcherData.unixTemplateUrl) 108 | configureTemplate(startScriptTask.windowsStartScriptGenerator, launcherData.windowsTemplateUrl) 109 | } 110 | } 111 | } 112 | 113 | @CompileDynamic 114 | private void configureCds() { 115 | if (cdsData.enabled) { 116 | this.doLast { 117 | project.exec { 118 | commandLine = ["$imageDir/bin/java", "-Xshare:dump"] 119 | } 120 | } 121 | System.properties['BADASS_CDS_ARCHIVE_FILE_LINUX'] = cdsData.sharedArchiveFile ?: '$APP_HOME/lib/server/$APP_NAME.jsa' 122 | System.properties['BADASS_CDS_ARCHIVE_FILE_WINDOWS'] = cdsData.sharedArchiveFile ?: '%~dp0\\server\\%~n0.jsa' 123 | } 124 | } 125 | 126 | void configureTemplate(ScriptGenerator scriptGenerator, URL template) { 127 | ((TemplateBasedScriptGenerator)scriptGenerator).template = project.resources.text.fromString(template.text) 128 | } 129 | 130 | @TaskAction 131 | void runtimeTaskAction() { 132 | def taskData = new RuntimeTaskData() 133 | taskData.distDir = distDir.asFile 134 | taskData.jreDir = jreDir.asFile 135 | taskData.imageDir = imageDir.asFile 136 | taskData.targetPlatforms = targetPlatforms.get() 137 | 138 | def taskImpl = new RuntimeTaskImpl(project, taskData) 139 | taskImpl.execute() 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/RuntimeZipTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.RuntimeZipTaskData 20 | import org.beryx.runtime.data.TargetPlatform 21 | import org.beryx.runtime.impl.RuntimeZipTaskImpl 22 | import org.gradle.api.file.Directory 23 | import org.gradle.api.file.RegularFile 24 | import org.gradle.api.provider.MapProperty 25 | import org.gradle.api.tasks.InputDirectory 26 | import org.gradle.api.tasks.Nested 27 | import org.gradle.api.tasks.OutputFile 28 | import org.gradle.api.tasks.TaskAction 29 | 30 | @CompileStatic 31 | class RuntimeZipTask extends BaseTask { 32 | @Nested 33 | MapProperty getTargetPlatforms() { 34 | extension.targetPlatforms 35 | } 36 | 37 | @InputDirectory 38 | Directory getImageDir() { 39 | extension.imageDir.get() 40 | } 41 | 42 | @OutputFile 43 | RegularFile getImageZip() { 44 | extension.imageZip.get() 45 | } 46 | 47 | RuntimeZipTask() { 48 | dependsOn(RuntimePlugin.TASK_NAME_RUNTIME) 49 | description = 'Creates a zip of the runtime image of your application' 50 | } 51 | 52 | @TaskAction 53 | void runtimeZipTaskAction() { 54 | def taskData = new RuntimeZipTaskData() 55 | taskData.targetPlatforms = targetPlatforms.get() 56 | taskData.imageDir = imageDir.asFile 57 | taskData.imageZip = imageZip.asFile 58 | def taskImpl = new RuntimeZipTaskImpl(project, taskData) 59 | taskImpl.execute() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/SuggestModulesTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.SuggestModulesData 20 | import org.beryx.runtime.impl.SuggestModulesTaskImpl 21 | import org.gradle.api.tasks.Input 22 | import org.gradle.api.tasks.TaskAction 23 | 24 | @CompileStatic 25 | class SuggestModulesTask extends BaseTask { 26 | @Input 27 | String getJavaHome() { 28 | javaHomeOrDefault 29 | } 30 | 31 | SuggestModulesTask() { 32 | dependsOn('jar') 33 | description = 'Suggests the modules to be included in the runtime image' 34 | outputs.upToDateWhen { false } 35 | } 36 | 37 | @TaskAction 38 | void suggestMergedModuleInfoAction() { 39 | def taskData = new SuggestModulesData() 40 | taskData.javaHome = javaHome 41 | def taskImpl = new SuggestModulesTaskImpl(project, taskData) 42 | taskImpl.execute() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/CdsData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import org.gradle.api.tasks.Input 20 | import org.gradle.api.tasks.Optional 21 | 22 | @CompileStatic 23 | class CdsData implements Serializable { 24 | @Input 25 | boolean enabled 26 | 27 | @Input @Optional 28 | String sharedArchiveFile 29 | } 30 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/JPackageData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import org.beryx.runtime.util.Util 19 | import org.gradle.api.tasks.Internal 20 | 21 | import static org.beryx.runtime.util.Util.EXEC_EXTENSION 22 | 23 | import groovy.transform.CompileStatic 24 | import groovy.transform.ToString 25 | 26 | import org.gradle.api.Project 27 | import org.gradle.api.tasks.Input 28 | import org.gradle.api.tasks.InputDirectory 29 | import org.gradle.api.tasks.Optional 30 | import org.gradle.api.tasks.OutputDirectory 31 | 32 | @CompileStatic 33 | @ToString(includeNames = true) 34 | class JPackageData { 35 | private final Project project 36 | private final LauncherData launcherData 37 | 38 | @Input 39 | String jpackageHome 40 | 41 | @Input 42 | String outputDir = 'jpackage' 43 | 44 | @Internal 45 | File imageOutputDir 46 | 47 | @Internal 48 | String imageName 49 | 50 | @Input 51 | List imageOptions = [] 52 | 53 | @InputDirectory @Optional 54 | File resourceDir 55 | 56 | @Input @Optional 57 | String targetPlatformName 58 | 59 | @Input 60 | boolean skipInstaller = false 61 | 62 | @Input @Optional 63 | String installerType 64 | 65 | @Internal 66 | File installerOutputDir 67 | 68 | @Internal 69 | String installerName 70 | 71 | @Input @Optional 72 | String appVersion 73 | 74 | @Input 75 | List installerOptions = [] 76 | 77 | @Internal 78 | List args = [] 79 | 80 | @Internal 81 | List jvmArgs = [] 82 | 83 | @Input @Optional 84 | String mainJar 85 | 86 | @Internal 87 | String mainClass 88 | 89 | JPackageData(Project project, LauncherData launcherData) { 90 | this.project = project 91 | this.launcherData = launcherData 92 | this.jpackageHome = '' 93 | } 94 | 95 | @Input 96 | List getArgsOrDefault() { 97 | this.@args ?: Util.getDefaultArgs(project) 98 | } 99 | 100 | @Input 101 | List getJvmArgsOrDefault() { 102 | this.@jvmArgs ?: launcherData.jvmArgsOrDefault 103 | } 104 | 105 | @Input 106 | String getMainClassOrDefault() { 107 | this.@mainClass ?: Util.getMainClass(project) 108 | } 109 | @Input 110 | String getImageNameOrDefault() { 111 | this.@imageName ?: project.name 112 | } 113 | 114 | @Input 115 | String getInstallerNameOrDefault() { 116 | this.@installerName ?: project.name 117 | } 118 | 119 | @OutputDirectory 120 | File getImageOutputDirOrDefault() { 121 | this.@imageOutputDir ?: project.file("$project.buildDir/$outputDir") 122 | } 123 | 124 | @OutputDirectory 125 | File getInstallerOutputDirOrDefault() { 126 | this.@installerOutputDir ?: project.file("$project.buildDir/$outputDir") 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/JPackageTaskData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | import org.beryx.runtime.JreTask 21 | import org.gradle.api.GradleException 22 | import org.gradle.api.logging.Logger 23 | import org.gradle.api.logging.Logging 24 | import org.gradle.internal.os.OperatingSystem 25 | 26 | @CompileStatic 27 | @ToString(includeNames = true) 28 | class JPackageTaskData { 29 | private static final Logger LOGGER = Logging.getLogger(JPackageTaskData.class) 30 | 31 | File distDir 32 | 33 | File jreDir 34 | 35 | File appImageDir 36 | 37 | JPackageData jpackageData 38 | String javaHome 39 | 40 | void configureAppImageDir() { 41 | final def imgOutDir = jpackageData.imageOutputDirOrDefault 42 | final def imageName = jpackageData.getImageNameOrDefault() 43 | final def appImagePath = "${imgOutDir}${File.separator}${imageName}" 44 | appImageDir = new File(appImagePath) 45 | 46 | if (OperatingSystem.current().macOsX) { 47 | if (!appImageDir.directory) { 48 | def currImagePath = "${appImagePath}.app" 49 | if (!new File(currImagePath).directory) { 50 | throw new GradleException("Unable to find the application image in ${imgOutDir}") 51 | } 52 | appImageDir = new File(currImagePath) 53 | } 54 | } 55 | } 56 | 57 | void configureRuntimeImageDir(JreTask jreTask) { 58 | def jlinkPlatforms = jreTask.targetPlatforms.get() 59 | if (jpackageData.targetPlatformName) { 60 | if (!jlinkPlatforms.isEmpty()) { 61 | if (!jlinkPlatforms.keySet().contains(jpackageData.targetPlatformName)) { 62 | throw new GradleException("The targetPlatform of the jpackage task ($jpackageData.targetPlatformName) doesn't match any of the targetPlatforms of the jlink task.") 63 | } 64 | } else { 65 | LOGGER.warn("No target platforms defined for the jlink task. The jpackage targetPlatform will be ignored.") 66 | jpackageData.targetPlatformName = null 67 | } 68 | } else { 69 | if (!jlinkPlatforms.isEmpty()) { 70 | if (jlinkPlatforms.size() > 1) { 71 | throw new GradleException("Since your runtime task is configured to generate images for multiple platforms, you must specify a targetPlatform for your jpackage task.") 72 | } 73 | jpackageData.targetPlatformName = jlinkPlatforms.keySet().first() 74 | LOGGER.warn("No target platform defined for the jpackage task. Defaulting to `$jpackageData.targetPlatformName`.") 75 | } 76 | } 77 | if (jpackageData.targetPlatformName) { 78 | jreDir = new File(jreTask.jreDirAsFile, "$jreTask.project.name-$jpackageData.targetPlatformName") 79 | } else { 80 | jreDir = jreTask.jreDirAsFile 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/JreTaskData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | 21 | @CompileStatic 22 | @ToString(includeNames = true) 23 | class JreTaskData { 24 | File jreDir 25 | List options 26 | boolean additive 27 | List modules 28 | String javaHome 29 | Map targetPlatforms 30 | } 31 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/LauncherData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | import org.beryx.runtime.RuntimePlugin 21 | import org.beryx.runtime.util.Util 22 | import org.gradle.api.GradleException 23 | import org.gradle.api.Project 24 | import org.gradle.api.tasks.Input 25 | import org.gradle.api.tasks.InputFile 26 | import org.gradle.api.tasks.Internal 27 | import org.gradle.api.tasks.Optional 28 | 29 | @CompileStatic 30 | @ToString(includeNames = true) 31 | class LauncherData { 32 | private final Project project 33 | 34 | @Internal 35 | List jvmArgs = [] 36 | 37 | LauncherData(Project project) { 38 | this.project = project 39 | } 40 | 41 | @Input 42 | boolean noConsole = false 43 | 44 | @Input 45 | boolean runInBinDir = false 46 | 47 | @InputFile @Optional 48 | File unixScriptTemplate 49 | 50 | @InputFile @Optional 51 | File windowsScriptTemplate 52 | 53 | @Input 54 | List getJvmArgsOrDefault() { 55 | this.@jvmArgs ?: Util.getDefaultJvmArgs(project) 56 | } 57 | 58 | @Internal 59 | URL getUnixTemplateUrl() { 60 | if(unixScriptTemplate) return unixScriptTemplate.toURI().toURL() 61 | def resourceName = '/unixScriptTemplate.txt' 62 | def templateUrl = RuntimePlugin.class.getResource(resourceName) 63 | if(!templateUrl) throw new GradleException("Resource $resourceName not found.") 64 | return templateUrl 65 | } 66 | 67 | @Internal 68 | URL getWindowsTemplateUrl() { 69 | if(windowsScriptTemplate) return windowsScriptTemplate.toURI().toURL() 70 | def resourceName = noConsole ? '/windowsScriptTemplateJavaw.txt' : '/windowsScriptTemplate.txt' 71 | def templateUrl = RuntimePlugin.class.getResource(resourceName) 72 | if(!templateUrl) throw new GradleException("Resource $resourceName not found.") 73 | return templateUrl 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/RuntimePluginExtension.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import org.beryx.runtime.util.Util 19 | import org.gradle.api.Action 20 | import org.gradle.api.Project 21 | import org.gradle.api.file.DirectoryProperty 22 | import org.gradle.api.file.RegularFileProperty 23 | import org.gradle.api.provider.ListProperty 24 | import org.gradle.api.provider.MapProperty 25 | import org.gradle.api.provider.Property 26 | 27 | import groovy.transform.CompileStatic 28 | 29 | @CompileStatic 30 | class RuntimePluginExtension { 31 | private final Project project 32 | 33 | final DirectoryProperty distDir 34 | final DirectoryProperty jreDir 35 | final DirectoryProperty imageDir 36 | final RegularFileProperty imageZip 37 | 38 | final ListProperty options 39 | final Property additive 40 | final ListProperty modules 41 | final Property javaHome 42 | final MapProperty targetPlatforms 43 | final Property jvmVersion 44 | 45 | final Property launcherData 46 | final Property jpackageData 47 | final Property cdsData 48 | 49 | RuntimePluginExtension(Project project) { 50 | this.project = project 51 | distDir = Util.createDirectoryProperty(project) 52 | 53 | jreDir = Util.createDirectoryProperty(project) 54 | jreDir.set(project.layout.buildDirectory.dir('jre')) 55 | 56 | imageDir = Util.createDirectoryProperty(project) 57 | imageDir.set(project.layout.buildDirectory.dir('image')) 58 | 59 | imageZip = Util.createRegularFileProperty(project) 60 | imageZip.set(project.layout.buildDirectory.file('image.zip')) 61 | 62 | options = project.objects.listProperty(String) 63 | options.set(new ArrayList()) 64 | 65 | additive = project.objects.property(Boolean) 66 | additive.set(false) 67 | 68 | modules = project.objects.listProperty(String) 69 | modules.set(new ArrayList()) 70 | 71 | javaHome = project.objects.property(String) 72 | 73 | targetPlatforms = project.objects.mapProperty(String, TargetPlatform) 74 | 75 | jvmVersion = project.objects.property(Integer) 76 | 77 | launcherData = project.objects.property(LauncherData) 78 | def ld = new LauncherData(project) 79 | launcherData.set(ld) 80 | 81 | jpackageData = project.objects.property(JPackageData) 82 | def jpd = new JPackageData(project, ld) 83 | jpackageData.set(jpd) 84 | 85 | cdsData = project.objects.property(CdsData) 86 | cdsData.set(new CdsData()) 87 | } 88 | 89 | void addOptions(String... options) { 90 | Util.addToListProperty(this.options, options) 91 | } 92 | 93 | void addModules(String... modules) { 94 | Util.addToListProperty(this.modules, modules) 95 | } 96 | 97 | void targetPlatform(String name, String jdkHome, List options = []) { 98 | targetPlatform(name) { TargetPlatform platform -> 99 | platform.jdkHome.set(jdkHome) 100 | platform.options.addAll(options) 101 | } 102 | } 103 | 104 | void targetPlatform(String name, Action action) { 105 | def targetPlatform = new TargetPlatform(project, name) 106 | targetPlatforms.put(name, targetPlatform) 107 | action.execute(targetPlatform) 108 | } 109 | 110 | void enableCds(Action action = null) { 111 | cdsData.get().enabled = true 112 | if(action) { 113 | action.execute(cdsData.get()) 114 | } 115 | } 116 | 117 | void launcher(Action action) { 118 | action.execute(launcherData.get()) 119 | } 120 | 121 | void jpackage(Action action) { 122 | action.execute(jpackageData.get()) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/RuntimeTaskData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | 21 | @CompileStatic 22 | @ToString(includeNames = true) 23 | class RuntimeTaskData { 24 | File distDir 25 | File jreDir 26 | File imageDir 27 | Map targetPlatforms 28 | } 29 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/RuntimeZipTaskData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | 21 | @CompileStatic 22 | @ToString(includeNames = true) 23 | class RuntimeZipTaskData { 24 | Map targetPlatforms 25 | File imageDir 26 | File imageZip 27 | } 28 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/SuggestModulesData.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.ToString 20 | 21 | @CompileStatic 22 | @ToString(includeNames = true) 23 | class SuggestModulesData { 24 | String javaHome 25 | } 26 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/data/TargetPlatform.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.data 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.util.JdkUtil 20 | import org.gradle.api.Project 21 | import org.gradle.api.internal.provider.DefaultProvider 22 | import org.gradle.api.logging.Logger 23 | import org.gradle.api.logging.Logging 24 | import org.gradle.api.provider.ListProperty 25 | import org.gradle.api.provider.Property 26 | import org.gradle.api.provider.Provider 27 | import org.gradle.api.tasks.Input 28 | 29 | @CompileStatic 30 | class TargetPlatform { 31 | static final Logger LOGGER = Logging.getLogger(TargetPlatform.class) 32 | 33 | private final Project project 34 | @Input 35 | final String name 36 | @Input 37 | final Property jdkHome 38 | @Input 39 | final ListProperty options 40 | 41 | TargetPlatform(Project project, String name) { 42 | this.name = name 43 | this.project = project 44 | jdkHome = project.objects.property(String) 45 | options = project.objects.listProperty(String) 46 | } 47 | 48 | void setJdkHome(Provider jdkHome) { 49 | this.jdkHome.set(jdkHome) 50 | } 51 | 52 | void addOptions(String... opts) { 53 | this.options.addAll(opts) 54 | } 55 | 56 | Provider jdkDownload(String downloadUrl, Closure downloadConfig = null) { 57 | def options = new JdkUtil.JdkDownloadOptions(project, name, downloadUrl) 58 | if(downloadConfig) { 59 | downloadConfig.delegate = options 60 | downloadConfig(options) 61 | } 62 | return new DefaultProvider({ 63 | def relativePathToHome = JdkUtil.downloadFrom(downloadUrl, options) 64 | def pathToHome = "$options.downloadDir/$relativePathToHome" 65 | LOGGER.info("Home of downloaded JDK distribution: $pathToHome") 66 | return pathToHome as String 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/BaseTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | 19 | import groovy.transform.CompileStatic 20 | import org.gradle.api.Project 21 | 22 | @CompileStatic 23 | class BaseTaskImpl { 24 | static String SEP = File.pathSeparatorChar 25 | 26 | final Project project 27 | final DATA td 28 | 29 | BaseTaskImpl(Project project, DATA td) { 30 | this.project = project 31 | this.td = td 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/JPackageImageTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import static org.beryx.runtime.util.Util.EXEC_EXTENSION 19 | 20 | import groovy.transform.CompileDynamic 21 | import groovy.transform.CompileStatic 22 | 23 | import org.beryx.runtime.data.JPackageTaskData 24 | import org.beryx.runtime.util.Util 25 | import org.gradle.api.GradleException 26 | import org.gradle.api.Project 27 | import org.gradle.api.logging.Logger 28 | import org.gradle.api.logging.Logging 29 | 30 | @CompileStatic 31 | class JPackageImageTaskImpl extends BaseTaskImpl { 32 | private static final Logger LOGGER = Logging.getLogger(JPackageImageTaskImpl.class) 33 | 34 | JPackageImageTaskImpl(Project project, JPackageTaskData taskData) { 35 | super(project, taskData) 36 | LOGGER.debug("taskData: $taskData") 37 | } 38 | 39 | @CompileDynamic 40 | void execute() { 41 | def result = project.exec { 42 | ignoreExitValue = true 43 | standardOutput = new ByteArrayOutputStream() 44 | 45 | project.ext.jpackageImageOutput = { 46 | return standardOutput.toString() 47 | } 48 | 49 | def jpd = td.jpackageData 50 | def outputDir = jpd.imageOutputDirOrDefault 51 | project.delete(outputDir) 52 | 53 | def jpackageExec = "${td.javaHome}/bin/jpackage$EXEC_EXTENSION" 54 | Util.checkExecutable(jpackageExec) 55 | 56 | def inputSuffix = project.tasks.findByName('installShadowDist') ? '-shadow' : '' 57 | LOGGER.info("input subdir: $project.name$inputSuffix") 58 | 59 | def appVersion = (jpd.appVersion ?: project.version).toString() 60 | def versionOpts = (appVersion == 'unspecified') ? [] : ['--app-version', appVersion] 61 | if (versionOpts && (!appVersion || !Character.isDigit(appVersion[0] as char))) { 62 | throw new GradleException("The first character of the --app-version argument should be a digit.") 63 | } 64 | 65 | final def resourceDir = jpd.getResourceDir() 66 | final def resourceOpts = resourceDir == null ? [] : [ '--resource-dir', resourceDir ] 67 | 68 | final def jvmArgs = (jpd.jvmArgsOrDefault ? jpd.jvmArgsOrDefault.collect{['--java-options', adjustArg(it) ]}.flatten() : []) 69 | final def args = (jpd.argsOrDefault ? jpd.argsOrDefault.collect{['--arguments', adjustArg(it)]}.flatten() : []) 70 | 71 | commandLine = [jpackageExec, 72 | '--type', 'app-image', 73 | '--input', "$td.distDir${File.separator}lib", 74 | '--main-jar', jpd.mainJar ?: Util.getMainDistJarFile(project).name, 75 | '--main-class', jpd.mainClassOrDefault, 76 | '--dest', outputDir, 77 | '--name', jpd.imageNameOrDefault, 78 | *versionOpts, 79 | '--runtime-image', td.jreDir, 80 | *resourceOpts, 81 | *jvmArgs, 82 | *args, 83 | *jpd.imageOptions] 84 | } 85 | 86 | if (result.exitValue != 0) { 87 | LOGGER.error(project.ext.jpackageImageOutput()) 88 | } else { 89 | LOGGER.info(project.ext.jpackageImageOutput()) 90 | } 91 | 92 | result.assertNormalExitValue() 93 | result.rethrowFailure() 94 | } 95 | 96 | static String adjustArg(String arg) { 97 | def adjusted = arg.replace('"', '\\"') 98 | if (!(adjusted ==~ /[\w\-\+=\/\\,;.:#]+/)) { 99 | adjusted = '"' + adjusted + '"' 100 | } 101 | // Workaround for https://bugs.openjdk.java.net/browse/JDK-8227641 102 | adjusted = adjusted.replace(' ', '\\" \\"') 103 | adjusted = adjusted.replace('{{BIN_DIR}}', '$APPDIR' + File.separator + '..') 104 | adjusted 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/JPackageTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import static org.beryx.runtime.util.Util.EXEC_EXTENSION 19 | 20 | import groovy.transform.CompileDynamic 21 | import groovy.transform.CompileStatic 22 | 23 | import org.beryx.runtime.data.JPackageTaskData 24 | import org.beryx.runtime.util.Util 25 | import org.gradle.api.GradleException 26 | import org.gradle.api.Project 27 | import org.gradle.api.logging.Logger 28 | import org.gradle.api.logging.Logging 29 | import org.gradle.internal.os.OperatingSystem 30 | 31 | @CompileStatic 32 | class JPackageTaskImpl extends BaseTaskImpl { 33 | private static final Logger LOGGER = Logging.getLogger(JPackageTaskImpl.class); 34 | 35 | JPackageTaskImpl(Project project, JPackageTaskData taskData) { 36 | super(project, taskData) 37 | LOGGER.debug("taskData: $taskData") 38 | } 39 | 40 | @CompileDynamic 41 | void execute() { 42 | final def jpd = td.jpackageData 43 | if (jpd.skipInstaller) { 44 | LOGGER.info("Skipping create-installer") 45 | return 46 | } 47 | 48 | 49 | def imgOutDir = jpd.imageOutputDirOrDefault 50 | def installerOutDir = jpd.installerOutputDirOrDefault 51 | if (imgOutDir != installerOutDir) { 52 | project.delete(project.files(installerOutDir)) 53 | } 54 | packageTypes.each { packageType -> 55 | if (imgOutDir != installerOutDir) { 56 | def subdirs = installerOutDir.listFiles({ f -> f.directory } as FileFilter) 57 | if(subdirs) project.delete(subdirs) 58 | } 59 | def result = project.exec { 60 | ignoreExitValue = true 61 | standardOutput = new ByteArrayOutputStream() 62 | project.ext.jpackageInstallerOutput = { 63 | return standardOutput.toString() 64 | } 65 | def jpackageExec = "${td.javaHome}/bin/jpackage$EXEC_EXTENSION" 66 | Util.checkExecutable(jpackageExec) 67 | 68 | def appVersion = (jpd.appVersion ?: project.version).toString() 69 | def versionOpts = (appVersion == 'unspecified') ? [] : ['--app-version', appVersion] 70 | if (versionOpts && (!appVersion || !Character.isDigit(appVersion[0] as char))) { 71 | throw new GradleException("The first character of the --app-version argument should be a digit.") 72 | } 73 | 74 | final def resourceDir = jpd.getResourceDir() 75 | final def resourceOpts = (resourceDir == null) ? [] : [ '--resource-dir', resourceDir ] 76 | 77 | commandLine = [jpackageExec, 78 | '--type', packageType, 79 | '--dest', jpd.getInstallerOutputDirOrDefault(), 80 | '--name', jpd.installerNameOrDefault, 81 | *versionOpts, 82 | '--app-image', td.appImageDir, 83 | *resourceOpts, 84 | *jpd.installerOptions] 85 | } 86 | 87 | if (result.exitValue != 0) { 88 | LOGGER.error(project.ext.jpackageInstallerOutput()) 89 | } else { 90 | LOGGER.info(project.ext.jpackageInstallerOutput()) 91 | } 92 | 93 | result.assertNormalExitValue() 94 | result.rethrowFailure() 95 | } 96 | } 97 | 98 | List getPackageTypes() { 99 | def jpd = td.jpackageData 100 | if (jpd.installerType) return [jpd.installerType] 101 | if (OperatingSystem.current().windows) { 102 | return ['exe', 'msi'] 103 | } else if(OperatingSystem.current().macOsX) { 104 | return ['pkg', 'dmg'] 105 | } else { 106 | return ['rpm', 'deb'] 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/JreTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.JreTaskData 20 | import org.beryx.runtime.util.SuggestedModulesBuilder 21 | import org.gradle.api.GradleException 22 | import org.gradle.api.Project 23 | import org.gradle.api.logging.Logger 24 | import org.gradle.api.logging.Logging 25 | 26 | class JreTaskImpl extends BaseTaskImpl { 27 | private static final Logger LOGGER = Logging.getLogger(JreTaskImpl) 28 | 29 | JreTaskImpl(Project project, JreTaskData taskData) { 30 | super(project, taskData) 31 | LOGGER.info("taskData: $taskData") 32 | } 33 | 34 | @CompileStatic 35 | void execute() { 36 | if(td.targetPlatforms) { 37 | td.targetPlatforms.values().each { platform -> 38 | File jreDir = new File(td.jreDir, "$project.name-$platform.name") 39 | createJre(jreDir, platform.jdkHome.getOrNull(), td.options + platform.options.get()) 40 | } 41 | } else { 42 | createJre(td.jreDir, td.javaHome, td.options) 43 | } 44 | } 45 | 46 | void createJre(File jreDir, String jdkHome, List options) { 47 | project.delete(jreDir) 48 | 49 | if(!project.file("$jdkHome/jmods").directory) { 50 | throw new GradleException("Directory not found: $jdkHome/jmods") 51 | } 52 | def cmd = ["$td.javaHome/bin/jlink", 53 | '-v', 54 | *options, 55 | '--module-path', 56 | "$jdkHome/jmods/", 57 | '--add-modules', modules.join(','), 58 | '--output', jreDir] 59 | LOGGER.info("Executing: $cmd") 60 | def result = project.exec { 61 | ignoreExitValue = true 62 | standardOutput = new ByteArrayOutputStream() 63 | project.ext.jlinkOutput = { 64 | return standardOutput.toString() 65 | } 66 | commandLine = cmd 67 | } 68 | if(result.exitValue != 0) { 69 | LOGGER.error(project.ext.jlinkOutput()) 70 | } else { 71 | LOGGER.info(project.ext.jlinkOutput()) 72 | } 73 | result.assertNormalExitValue() 74 | result.rethrowFailure() 75 | } 76 | 77 | @CompileStatic 78 | Collection getModules() { 79 | Set imageModules = [] 80 | if(td.additive || !td.modules) { 81 | imageModules.addAll(new SuggestedModulesBuilder(td.javaHome).getProjectModules(project)) 82 | } 83 | if(td.modules) { 84 | imageModules.addAll(td.modules) 85 | } 86 | imageModules 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/RuntimeTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import groovy.transform.CompileStatic 19 | import org.beryx.runtime.data.RuntimeTaskData 20 | import org.gradle.api.Project 21 | import org.gradle.api.logging.Logger 22 | import org.gradle.api.logging.Logging 23 | 24 | class RuntimeTaskImpl extends BaseTaskImpl { 25 | private static final Logger LOGGER = Logging.getLogger(RuntimeTaskImpl) 26 | 27 | RuntimeTaskImpl(Project project, RuntimeTaskData taskData) { 28 | super(project, taskData) 29 | LOGGER.info("taskData: $taskData") 30 | } 31 | 32 | @CompileStatic 33 | void execute() { 34 | if(td.targetPlatforms) { 35 | td.targetPlatforms.values().each { platform -> 36 | File jreDir = new File(td.jreDir, "$project.name-$platform.name") 37 | File imageDir = new File(td.imageDir, "$project.name-$platform.name") 38 | createRuntime(jreDir, imageDir) 39 | } 40 | } else { 41 | createRuntime(td.jreDir, td.imageDir) 42 | } 43 | } 44 | 45 | void createRuntime(File jreDir, File imageDir) { 46 | project.delete(imageDir) 47 | copyJre(jreDir, imageDir) 48 | copyAppTo(imageDir) 49 | } 50 | 51 | void copyJre(File jreDir, File imageDir) { 52 | project.copy { 53 | from jreDir 54 | into imageDir 55 | } 56 | } 57 | 58 | void copyAppTo(File imageDir) { 59 | project.copy { 60 | from td.distDir 61 | into imageDir 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/RuntimeZipTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import org.beryx.runtime.data.RuntimeZipTaskData 19 | import org.gradle.api.Project 20 | import org.gradle.api.logging.Logger 21 | import org.gradle.api.logging.Logging 22 | 23 | class RuntimeZipTaskImpl extends BaseTaskImpl { 24 | private static final Logger LOGGER = Logging.getLogger(RuntimeZipTaskImpl) 25 | 26 | RuntimeZipTaskImpl(Project project, RuntimeZipTaskData taskData) { 27 | super(project, taskData) 28 | LOGGER.info("taskData: $taskData") 29 | } 30 | 31 | void execute() { 32 | if(td.targetPlatforms) { 33 | def zipDir = td.imageZip.parentFile 34 | def zipName = td.imageZip.name 35 | int pos = zipName.lastIndexOf('.') 36 | def ext = (pos > 0) ? zipName.substring(pos+1) : 'zip' 37 | def baseName = (pos > 0) ? zipName.substring(0,pos) : zipName 38 | td.targetPlatforms.values().each { platform -> 39 | File zipFile = new File(zipDir, "${baseName}-${platform.name}.${ext}") 40 | File imageDir = new File(td.imageDir, "$project.name-$platform.name") 41 | createZip(imageDir, zipFile) 42 | } 43 | } else { 44 | createZip(td.imageDir, td.imageZip) 45 | } 46 | } 47 | 48 | private void createZip(File imageDir, File zipFile) { 49 | def parentPath = imageDir.parentFile.toPath() 50 | project.ant.zip(destfile: zipFile, duplicate: 'fail') { 51 | imageDir.eachFileRecurse { f -> 52 | int mode = f.canExecute() ? 755 : 644 53 | def relPath = parentPath.relativize(f.toPath()).toString() 54 | zipfileset(dir: parentPath, includes: relPath, filemode: mode) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/impl/SuggestModulesTaskImpl.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.impl 17 | 18 | import org.beryx.runtime.data.SuggestModulesData 19 | import org.beryx.runtime.util.SuggestedModulesBuilder 20 | import org.gradle.api.Project 21 | import org.gradle.api.logging.Logger 22 | import org.gradle.api.logging.Logging 23 | 24 | class SuggestModulesTaskImpl extends BaseTaskImpl { 25 | private static final Logger LOGGER = Logging.getLogger(SuggestModulesTaskImpl) 26 | 27 | SuggestModulesTaskImpl(Project project, SuggestModulesData taskData) { 28 | super(project, taskData) 29 | LOGGER.info("taskData: $taskData") 30 | } 31 | 32 | void execute() { 33 | def modules = new SuggestedModulesBuilder(td.javaHome).getProjectModules(project) 34 | println "modules = [\n${modules.collect {'\'' + it + '\''}.join(',\n')}]" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/JdkUtil.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileStatic 19 | import org.gradle.api.GradleException 20 | import org.gradle.api.Project 21 | import org.gradle.api.file.CopySpec 22 | import org.gradle.api.logging.Logger 23 | import org.gradle.api.logging.Logging 24 | 25 | @CompileStatic 26 | class JdkUtil { 27 | private static final Logger LOGGER = Logging.getLogger(JdkUtil.class) 28 | 29 | static class JdkDownloadOptions implements Serializable { 30 | transient final Project project 31 | String downloadDir 32 | String archiveName 33 | String archiveExtension 34 | String pathToHome 35 | boolean overwrite 36 | 37 | JdkDownloadOptions(Project project, String targetPlatform, String downloadUrl) { 38 | this.project = project 39 | this.downloadDir = "$project.buildDir/jdks/$targetPlatform" 40 | this.archiveName = "jdk" 41 | def urlPath = new URL(downloadUrl).path 42 | if(urlPath.endsWith(".tar.gz")) archiveExtension = "tar.gz" 43 | if(urlPath.endsWith(".zip")) archiveExtension = "zip" 44 | } 45 | 46 | void validate() { 47 | if(!project) throw new GradleException("Internal error: project not set in JdkDownloadOptions") 48 | if(!downloadDir) throw new GradleException("Please provide a value for 'downloadDir' when calling the 'jdkDownload' method") 49 | if(!archiveName) throw new GradleException("Please provide a value for 'archiveName' when calling the 'jdkDownload' method") 50 | if(!archiveExtension) throw new GradleException("Cannot infer the archive type. Please provide a value for 'archiveExtension' when calling the 'jdkDownload' method. Accepted values: 'tar.gz', 'zip'") 51 | } 52 | } 53 | 54 | static String downloadFrom(String url, JdkDownloadOptions options) { 55 | options.validate() 56 | def archiveFile = new File("${options.downloadDir}/${options.archiveName}.${options.archiveExtension}") 57 | def archiveDir = archiveFile.parentFile 58 | maybeCreateDir(archiveDir) 59 | if(options.overwrite) { 60 | archiveFile.delete() 61 | if(archiveFile.exists()) throw new GradleException("Cannot delete $archiveFile") 62 | } else { 63 | def pathToHome = options.pathToHome ?: detectPathToHome(archiveDir) 64 | if(pathToHome) { 65 | LOGGER.info("JDK found at $archiveDir/$pathToHome; skipping download from $url") 66 | return pathToHome 67 | } 68 | } 69 | if(!archiveFile.file) { 70 | LOGGER.info("Downloading from $url into $archiveFile") 71 | new URL(url).withInputStream{ is -> archiveFile.withOutputStream{ it << is }} 72 | } 73 | return unpackJdk(archiveDir, archiveFile, options) 74 | } 75 | 76 | private static String unpackJdk(File archiveDir, File archiveFile, JdkDownloadOptions options) { 77 | if(!archiveFile.file) throw new GradleException("Archive file $archiveFile does not exist.") 78 | LOGGER.info("Unpacking $archiveFile") 79 | options.project.copy { CopySpec spec -> 80 | spec.from ((options.archiveExtension == 'tar.gz') 81 | ? options.project.tarTree(options.project.resources.gzip(archiveFile)) 82 | : options.project.zipTree(archiveFile)) 83 | spec.into archiveDir 84 | } 85 | if(options.pathToHome) { 86 | if(!isJdkHome(new File("$archiveDir/$options.pathToHome"))) { 87 | LOGGER.warn("JDK home not found at $archiveDir/$options.pathToHome! Please check the the unpacked archive.") 88 | } 89 | return options.pathToHome 90 | } else { 91 | def pathToHome = detectPathToHome(archiveDir) 92 | if(!pathToHome) throw new GradleException("Cannot detect JDK home in $archiveDir. Check the unpacked archive and/or try setting `pathToHome` when calling the 'jdkDownload' method") 93 | return pathToHome 94 | } 95 | } 96 | 97 | private static String detectPathToHome(File dir) { 98 | def dirs = [dir] 99 | dir.eachDirRecurse { dirs << it } 100 | def homeDir = dirs.find { isJdkHome(it) } 101 | if(!homeDir) return null 102 | def relPath = dir.toPath().relativize(homeDir.toPath()) 103 | return relPath ?: '.' 104 | } 105 | 106 | private static boolean isJdkHome(File dir) { 107 | new File("$dir/bin/java").file || new File("$dir/bin/java.exe").file 108 | } 109 | 110 | private static void maybeCreateDir(File dir) { 111 | if (!dir.directory) { 112 | if (dir.exists()) throw new GradleException("$dir exists but it's not a directory.") 113 | dir.mkdirs() 114 | if (!dir.directory) throw new GradleException("Cannot create directory $dir.") 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/ModuleManager.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileStatic 19 | import org.gradle.api.logging.Logger 20 | import org.gradle.api.logging.Logging 21 | 22 | @CompileStatic 23 | class ModuleManager { 24 | private static final Logger LOGGER = Logging.getLogger(ModuleManager.class); 25 | 26 | final String javaHome 27 | 28 | ModuleManager(String javaHome) { 29 | this.javaHome = javaHome 30 | } 31 | 32 | /** 33 | * @return map with entries of type [package : module-name] 34 | */ 35 | Map getExportsMap(String... modulePaths) { 36 | final Map exportMap = [:] 37 | def runner = new SourceCodeRunner(javaHome, 'ModuleManager', sourceCode) 38 | String output = runner.getOutput(modulePaths); 39 | output.eachLine {line -> 40 | if(line) { 41 | def nameAndExports = line.split(':') 42 | if(nameAndExports.length > 1) { 43 | String moduleName = nameAndExports[0] 44 | for(String exportedPkg: nameAndExports[1].split(',')) { 45 | exportMap[exportedPkg] = moduleName 46 | } 47 | } 48 | } 49 | } 50 | exportMap 51 | } 52 | 53 | static final String sourceCode = ''' 54 | import java.io.File; 55 | import java.io.FileInputStream; 56 | import java.io.IOException; 57 | import java.io.InputStream; 58 | import java.lang.module.ModuleDescriptor; 59 | import java.util.ArrayList; 60 | import java.util.Arrays; 61 | import java.util.Enumeration; 62 | import java.util.List; 63 | import java.util.stream.Collectors; 64 | import java.util.zip.ZipEntry; 65 | import java.util.zip.ZipFile; 66 | 67 | public class ModuleManager { 68 | public static void main(String[] args) { 69 | List exports = getExports(getJarsAndMods(args)); 70 | System.out.println(exports.stream().collect(Collectors.joining("\\n"))); 71 | } 72 | 73 | private static List getJarsAndMods(String... modulePaths) { 74 | List allFiles = new ArrayList<>(); 75 | for(String path: modulePaths) { 76 | File f = new File(path); 77 | if(f.isFile()) allFiles.add(f); 78 | if(f.isDirectory()) { 79 | allFiles.addAll(Arrays.asList(f.listFiles(File::isFile))); 80 | } 81 | } 82 | return allFiles.stream() 83 | .filter(f -> f.getName().endsWith(".jar") || f.getName().endsWith(".jmod")) 84 | .collect(Collectors.toList()); 85 | } 86 | 87 | private static List getExports(List files) { 88 | List exports = new ArrayList<>(); 89 | for(File f: files) { 90 | ModuleDescriptor md = getModuleDescriptor(f); 91 | if(md != null && !md.exports().isEmpty()) { 92 | String exportedPackages = md.exports().stream() 93 | .map(ModuleDescriptor.Exports::source) 94 | .collect(Collectors.joining(",")); 95 | exports.add(md.name() + ":" + exportedPackages); 96 | } 97 | } 98 | return exports; 99 | } 100 | 101 | private static ModuleDescriptor getModuleDescriptor(File f) { 102 | try { 103 | if(!f.isFile()) throw new IllegalArgumentException(f + " is not a file"); 104 | if(f.getName().equals("module-info.class")) { 105 | return ModuleDescriptor.read(new FileInputStream(f)); 106 | } 107 | if(!f.getName().endsWith(".jar") && !f.getName().endsWith(".jmod")) throw new IllegalArgumentException("Unsupported file type: " + f); 108 | String prefix = f.getName().endsWith(".jmod") ? "classes/" : ""; 109 | ZipFile zipFile = new ZipFile(f); 110 | for (Enumeration entries = zipFile.entries(); entries.hasMoreElements();) { 111 | ZipEntry entry = entries.nextElement(); 112 | if(entry.getName().equals(prefix + "module-info.class")) { 113 | InputStream entryStream = zipFile.getInputStream(entry); 114 | return ModuleDescriptor.read(entryStream); 115 | } 116 | } 117 | return null; 118 | } catch (IOException e) { 119 | throw new RuntimeException(e); 120 | } 121 | } 122 | } 123 | ''' 124 | } 125 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/PackageCollection.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileDynamic 19 | import groovy.transform.CompileStatic 20 | import groovy.transform.ToString 21 | import org.objectweb.asm.Type 22 | import org.gradle.api.logging.Logger 23 | import org.gradle.api.logging.Logging 24 | 25 | @ToString 26 | @CompileStatic 27 | class PackageCollection { 28 | private static final Logger LOGGER = Logging.getLogger(PackageCollection.class); 29 | 30 | final TreeSet packages = new TreeSet() 31 | 32 | // final TreeSet packages = new TreeSet() { 33 | // @Override 34 | // boolean add(Object o) { 35 | // if(String.valueOf(o).startsWith('L')) { 36 | // println "#### $o" 37 | // } 38 | // return super.add(o) 39 | // } 40 | // } 41 | 42 | void addPackage(String pkg) { 43 | packages << adjust(pkg) 44 | } 45 | 46 | @CompileDynamic 47 | void addDescriptor(String descriptor) { 48 | addTypes(Type.getType(descriptor)) 49 | } 50 | 51 | @CompileDynamic 52 | void addTypes(Type... types) { 53 | types.each { Type type -> 54 | switch(type.sort) { 55 | case Type.ARRAY: addTypes(type.elementType); break 56 | case Type.OBJECT: addClass(type.className); break; 57 | case Type.METHOD: addTypes(*type.argumentTypes, type.returnType); break; 58 | } 59 | } 60 | } 61 | 62 | void addClass(String className) { 63 | if(!className) return 64 | if(className.startsWith('[')) { 65 | addDescriptor(className) 66 | } else { 67 | def pkg = removeFromLastOccurrence(adjust(className), '.') 68 | if(pkg) packages << pkg 69 | } 70 | } 71 | 72 | static String removeFromLastOccurrence(String s, String delimiter) { 73 | int pos = s.lastIndexOf(delimiter) 74 | if(pos <= 0) { 75 | LOGGER.debug("Cannot remove from last occurrence of $delimiter in $s") 76 | return null 77 | } 78 | s.substring(0, pos) 79 | } 80 | 81 | static String adjust(String s) { 82 | s = s?.trim() 83 | if(!s) return '' 84 | while(!Character.isJavaIdentifierStart(s[0] as char)) { 85 | s = s.substring(1) 86 | if(!s) return '' 87 | } 88 | while(!Character.isJavaIdentifierPart(s[s.length() - 1] as char)) { 89 | s = s.substring(0, s.length()-1) 90 | if(!s) return '' 91 | } 92 | s.replace('/', '.') 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/PackageUseScanner.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileDynamic 19 | import groovy.transform.CompileStatic 20 | import org.gradle.api.logging.Logger 21 | import org.gradle.api.logging.Logging 22 | import org.objectweb.asm.* 23 | 24 | @CompileStatic 25 | class PackageUseScanner extends ClassVisitor { 26 | private static final Logger LOGGER = Logging.getLogger(PackageUseScanner.class); 27 | 28 | final PackageCollection usedPackages = new PackageCollection() 29 | final PackageCollection ownPackages = new PackageCollection() 30 | 31 | private String currentClassName 32 | 33 | private class ScannerMethodVisitor extends MethodVisitor { 34 | ScannerMethodVisitor(MethodVisitor methodVisitor) { 35 | super(Opcodes.ASM9, methodVisitor) 36 | } 37 | 38 | @Override 39 | void visitTypeInsn(int opcode, String type) { 40 | LOGGER.debug "visitTypeInsn($type)" 41 | usedPackages.addClass(type) 42 | super.visitTypeInsn(opcode, type) 43 | } 44 | 45 | @Override 46 | void visitMethodInsn(int opcode, String owner, String name, String descriptor) { 47 | visitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE) 48 | } 49 | 50 | @Override 51 | void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 52 | LOGGER.debug "visitMethodInsn($owner)" 53 | usedPackages.addClass(owner) 54 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) 55 | } 56 | 57 | @Override 58 | void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 59 | LOGGER.debug "visitInvokeDynamic($descriptor)" 60 | usedPackages.addDescriptor(descriptor) 61 | super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments) 62 | } 63 | 64 | @Override 65 | void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 66 | LOGGER.debug "visitLocalVariable($descriptor)" 67 | usedPackages.addDescriptor(descriptor) 68 | super.visitLocalVariable(name, descriptor, signature, start, end, index) 69 | } 70 | 71 | @Override 72 | void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 73 | LOGGER.debug "visitMultiANewArrayInsn($descriptor)" 74 | usedPackages.addDescriptor(descriptor) 75 | super.visitMultiANewArrayInsn(descriptor, numDimensions) 76 | } 77 | 78 | @Override 79 | AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 80 | LOGGER.debug "visitTypeAnnotation($descriptor)" 81 | usedPackages.addDescriptor(descriptor) 82 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) 83 | } 84 | 85 | @Override 86 | AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 87 | LOGGER.debug "visitAnnotation($descriptor)" 88 | usedPackages.addDescriptor(descriptor) 89 | return super.visitAnnotation(descriptor, visible) 90 | } 91 | 92 | @Override 93 | AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 94 | LOGGER.debug "visitInsnAnnotation($descriptor)" 95 | usedPackages.addDescriptor(descriptor) 96 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible) 97 | } 98 | 99 | @Override 100 | AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 101 | LOGGER.debug "visitLocalVariablAnnotation($descriptor)" 102 | usedPackages.addDescriptor(descriptor) 103 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible) 104 | } 105 | 106 | @Override 107 | AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 108 | LOGGER.debug "visitParameterAnnotation($descriptor)" 109 | usedPackages.addDescriptor(descriptor) 110 | return super.visitParameterAnnotation(parameter, descriptor, visible) 111 | } 112 | 113 | @Override 114 | AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 115 | LOGGER.debug "visitTrayCatchAnnotation($descriptor)" 116 | usedPackages.addDescriptor(descriptor) 117 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible) 118 | } 119 | } 120 | 121 | 122 | PackageUseScanner() { 123 | super(Opcodes.ASM9) 124 | } 125 | 126 | Set getExternalPackages() { 127 | (usedPackages.packages - ownPackages.packages) as Set 128 | } 129 | 130 | @Override 131 | void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 132 | LOGGER.debug "Visiting $name : $superName" 133 | currentClassName = name 134 | ownPackages.addClass(name) 135 | if(superName) usedPackages.addClass(superName) 136 | for(intf in interfaces) {usedPackages.addClass(intf)} 137 | super.visit(version, access, name, signature, superName, interfaces) 138 | } 139 | 140 | @Override 141 | void visitEnd() { 142 | LOGGER.debug "End visiting $currentClassName" 143 | currentClassName = null 144 | super.visitEnd() 145 | } 146 | 147 | @Override 148 | FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 149 | usedPackages.addDescriptor(descriptor) 150 | return super.visitField(access, name, descriptor, signature, value) 151 | } 152 | 153 | @Override 154 | MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 155 | if(exceptions) { 156 | for(e in exceptions) { 157 | usedPackages.addClass(e) 158 | } 159 | } 160 | usedPackages.addDescriptor(descriptor) 161 | def mv = super.visitMethod(access, name, descriptor, signature, exceptions) 162 | new ScannerMethodVisitor(mv) 163 | } 164 | 165 | @Override 166 | void visitInnerClass(String name, String outerName, String innerName, int access) { 167 | usedPackages.addClass(name) 168 | super.visitInnerClass(name, outerName, innerName, access) 169 | } 170 | 171 | @Override 172 | void visitNestHost(String nestHost) { 173 | LOGGER.debug "visitNestHost($nestHost)" 174 | usedPackages.addClass(nestHost) 175 | super.visitNestHost(nestHost) 176 | } 177 | 178 | @Override 179 | void visitNestMember(String nestMember) { 180 | LOGGER.debug "visitNestMember($nestMember)" 181 | usedPackages.addClass(nestMember) 182 | super.visitNestMember(nestMember) 183 | } 184 | 185 | @Override 186 | void visitOuterClass(String owner, String name, String descriptor) { 187 | usedPackages.addClass(owner) 188 | super.visitOuterClass(owner, name, descriptor) 189 | } 190 | 191 | @Override 192 | AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 193 | usedPackages.addDescriptor(descriptor) 194 | return super.visitAnnotation(descriptor, visible) 195 | } 196 | 197 | @Override 198 | AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 199 | usedPackages.addDescriptor(descriptor) 200 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) 201 | } 202 | 203 | List scan(File file) { 204 | List invalidEntries = [] 205 | LOGGER.debug("Scanning $file") 206 | Util.scan(file, { String basePath, String path, InputStream inputStream -> 207 | if(Util.isValidClassFileReference(path)) { 208 | LOGGER.trace("processing: $path") 209 | try { 210 | ClassReader cr = new ClassReader(inputStream) 211 | cr.accept(this, 0) 212 | } catch (Exception e) { 213 | LOGGER.info("Failed to scan $path", e) 214 | invalidEntries << ("${basePath}/${path}" as String) 215 | return 216 | } 217 | } 218 | } as Closure) 219 | invalidEntries 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/SourceCodeRunner.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileStatic 19 | import org.gradle.api.GradleException 20 | import org.gradle.api.logging.Logger 21 | import org.gradle.api.logging.Logging 22 | 23 | import java.nio.file.Files 24 | import java.util.concurrent.TimeUnit 25 | 26 | @CompileStatic 27 | class SourceCodeRunner { 28 | private static final Logger LOGGER = Logging.getLogger(SourceCodeRunner.class); 29 | 30 | final String javaHome 31 | final String className 32 | final String sourceCode 33 | long timeoutSeconds = 120 34 | 35 | SourceCodeRunner(String javaHome, String className, String sourceCode) { 36 | this.javaHome = javaHome 37 | this.className = className 38 | this.sourceCode = sourceCode 39 | } 40 | 41 | String getOutput(String[] args) { 42 | def path = Files.createTempDirectory("badass-") 43 | def javaFile = path.resolve("${className}.java").toFile() 44 | javaFile << sourceCode 45 | 46 | def javacCmd = "$javaHome/bin/javac -cp . -d . ${className}.java" 47 | LOGGER.info("Executing: $javacCmd") 48 | def javacProc = javacCmd.execute(null as String[], path.toFile()) 49 | def javacErrOutput = new StringBuilder() 50 | Thread javacErrThread = javacProc.consumeProcessErrorStream(javacErrOutput) 51 | if (!javacProc.waitFor(timeoutSeconds, TimeUnit.SECONDS)) { 52 | throw new GradleException("javac ${className}.java hasn't exited after $timeoutSeconds seconds.") 53 | } 54 | javacErrThread.join() 55 | String javacOutput = javacProc.text 56 | javacProc.closeStreams() 57 | LOGGER.info(javacOutput) 58 | if (javacProc.exitValue()) { 59 | throw new GradleException("javac ${className}.java failed: $javacErrOutput") 60 | } 61 | if (javacErrOutput.size() > 0) LOGGER.error("javac failed: $javacErrOutput") 62 | 63 | def cmdArray = ["$javaHome/bin/java", "-cp", ".", "${className}"] 64 | if(args) { 65 | cmdArray.addAll(Arrays.asList(args)) 66 | } 67 | LOGGER.info("Executing: $cmdArray") 68 | def javaProc = Runtime.runtime.exec((cmdArray as String[]), null, path.toFile()) 69 | 70 | def javaErrOutput = new StringBuilder() 71 | def javaOutput = new StringBuilder() 72 | Thread javaOutThread = javaProc.consumeProcessOutputStream(javaOutput) 73 | Thread javaErrThread = javaProc.consumeProcessErrorStream(javaErrOutput) 74 | if (!javaProc.waitFor(timeoutSeconds, TimeUnit.SECONDS)) { 75 | throw new GradleException("java ${className} hasn't exited after $timeoutSeconds seconds.") 76 | } 77 | javaOutThread.join() 78 | javaErrThread.join() 79 | javaProc.closeStreams() 80 | 81 | LOGGER.info(javaOutput.toString()) 82 | if (javaProc.exitValue()) { 83 | throw new GradleException("java ${className} failed: $javaErrOutput") 84 | } 85 | if (javaErrOutput.size() > 0) LOGGER.error("java failed: $javaErrOutput") 86 | return javaOutput.toString() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/SuggestedModulesBuilder.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.transform.CompileStatic 19 | import org.gradle.api.Project 20 | import org.gradle.api.artifacts.ResolvedDependency 21 | import org.gradle.api.logging.Logger 22 | import org.gradle.api.logging.Logging 23 | 24 | @CompileStatic 25 | class SuggestedModulesBuilder { 26 | private static final Logger LOGGER = Logging.getLogger(SuggestedModulesBuilder) 27 | 28 | final String javaHome 29 | 30 | SuggestedModulesBuilder(String javaHome) { 31 | this.javaHome = javaHome 32 | } 33 | 34 | Set getProjectModules(Project project) { 35 | Set modules = [] 36 | def mainDistJarFile = Util.getMainDistJarFile(project) 37 | modules.addAll(getModulesRequiredBy(mainDistJarFile)) 38 | for(ResolvedDependency dep: project.configurations['runtimeClasspath'].resolvedConfiguration.firstLevelModuleDependencies) { 39 | def f = Util.getArtifact(dep) 40 | if(f) modules.addAll(getModulesRequiredBy(f)) 41 | } 42 | if(!modules) { 43 | modules << 'java.base' 44 | } 45 | modules 46 | } 47 | 48 | Set getModulesRequiredBy(File jarOrDir) { 49 | LOGGER.debug("Detecting modules required by $jarOrDir") 50 | def scanner = new PackageUseScanner() 51 | def invalidFiles = scanner.scan(jarOrDir) 52 | if(invalidFiles) { 53 | LOGGER.warn("Failed to scan: $invalidFiles") 54 | } 55 | LOGGER.debug("External packages used by the merged service:\n\t${scanner.externalPackages.join('\n\t')}") 56 | 57 | def exportMap = new ModuleManager(javaHome).getExportsMap("$javaHome/jmods") 58 | def modules = new HashSet() 59 | 60 | scanner.externalPackages.each { pkg -> 61 | def moduleName = exportMap[pkg] 62 | if(!moduleName) { 63 | LOGGER.info("Cannot find module exporting $pkg") 64 | } else if(moduleName != 'java.base'){ 65 | modules << moduleName 66 | } 67 | } 68 | modules 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/groovy/org/beryx/runtime/util/Util.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime.util 17 | 18 | import groovy.io.FileType 19 | import groovy.transform.CompileDynamic 20 | import groovy.transform.CompileStatic 21 | import groovy.transform.stc.ClosureParams 22 | import groovy.transform.stc.SimpleType 23 | import org.codehaus.groovy.tools.Utilities 24 | import org.gradle.api.GradleException 25 | import org.gradle.api.Project 26 | import org.gradle.api.artifacts.ResolvedDependency 27 | import org.gradle.api.file.DirectoryProperty 28 | import org.gradle.api.file.RegularFileProperty 29 | import org.gradle.api.logging.Logger 30 | import org.gradle.api.logging.Logging 31 | import org.gradle.api.plugins.ApplicationPluginConvention 32 | import org.gradle.api.plugins.JavaPlugin 33 | import org.gradle.api.plugins.JavaPluginExtension 34 | import org.gradle.api.provider.ListProperty 35 | import org.gradle.api.provider.Property 36 | import org.gradle.api.provider.Provider 37 | import org.gradle.api.tasks.bundling.Jar 38 | import org.gradle.jvm.toolchain.JavaToolchainService 39 | 40 | import java.util.zip.ZipEntry 41 | import java.util.zip.ZipFile 42 | 43 | @CompileStatic 44 | class Util { 45 | private static final Logger LOGGER = Logging.getLogger(Util) 46 | static String EXEC_EXTENSION = System.getProperty('os.name', '').toLowerCase().contains('win') ? '.exe' : '' 47 | 48 | static boolean isValidClassFileReference(String name) { 49 | if(!name.endsWith('.class')) return false 50 | name = name - '.class' 51 | name = name.split('[./\\\\]')[-1] 52 | return Utilities.isJavaIdentifier(name) 53 | } 54 | 55 | static void scan(File file, 56 | @ClosureParams(value= SimpleType, options="java.lang.String,java.lang.String,java.io.InputStream") Closure action) { 57 | if(!file.exists()) throw new IllegalArgumentException("File or directory not found: $file") 58 | if(file.directory) scanDir(file, action) 59 | else scanJar(file, action) 60 | } 61 | 62 | private static void scanDir(File dir, 63 | @ClosureParams(value= SimpleType, options="java.lang.String,java.lang.String,java.io.InputStream") Closure action) { 64 | if(!dir.directory) throw new IllegalArgumentException("Not a directory: $dir") 65 | dir.eachFileRecurse(FileType.FILES) { file -> 66 | def basePath = dir.absolutePath.replace('\\', '/') 67 | def relPath = dir.toPath().relativize(file.toPath()).toString().replace('\\', '/') 68 | action.call(basePath, relPath, file.newInputStream()) 69 | } 70 | } 71 | 72 | private static void scanJar(File jarFile, 73 | @ClosureParams(value= SimpleType, options="java.lang.String,java.lang.String,java.io.InputStream") Closure action) { 74 | def zipFile = new ZipFile(jarFile) 75 | zipFile.entries().each { ZipEntry entry -> action.call('', entry.name, zipFile.getInputStream(entry)) } 76 | } 77 | 78 | static File getArtifact(ResolvedDependency dep) { 79 | def artifact = dep.moduleArtifacts.find {it.classifier} ?: dep.moduleArtifacts?.getAt(0) 80 | if(artifact) return artifact.file 81 | LOGGER.info "Cannot retrieve artifact $dep.name" 82 | return null 83 | } 84 | 85 | static DirectoryProperty createDirectoryProperty(Project project) { 86 | return project.objects.directoryProperty() 87 | } 88 | 89 | static RegularFileProperty createRegularFileProperty(Project project) { 90 | return project.objects.fileProperty() 91 | } 92 | 93 | static void addToListProperty(ListProperty listProp, T... values) { 94 | listProp.addAll(values as List) 95 | } 96 | 97 | static Provider> createMapProperty(Project project, 98 | Class keyType, Class valueType) { 99 | Provider> provider = project.objects.mapProperty(keyType, valueType) 100 | provider.set(new TreeMap()) 101 | provider 102 | } 103 | 104 | @CompileDynamic 105 | static void putToMapProvider(Provider> mapProvider, K key, V value) { 106 | def map = new TreeMap(mapProvider.get()) 107 | map[key] = value 108 | mapProvider.set(map) 109 | } 110 | 111 | static void checkExecutable(String filePath) { 112 | checkExecutable(new File(filePath)) 113 | } 114 | 115 | static void checkExecutable(File f) { 116 | if(!f.file) throw new GradleException("$f.absolutePath does not exist.") 117 | if(!f.canExecute()) throw new GradleException("$f.absolutePath is not executable.") 118 | } 119 | 120 | static File getArchiveFile(Project project) { 121 | Jar jarTask = (Jar)project.tasks.getByName(JavaPlugin.JAR_TASK_NAME) 122 | return jarTask.archiveFile.get().asFile 123 | } 124 | 125 | static File getMainDistJarFile(Project project) { 126 | File jarFile = getArchiveFile(project) 127 | if(project.tasks.findByName('installShadowDist')) { 128 | def baseName = jarFile.name - '.jar' 129 | jarFile = new File(jarFile.parent, "$baseName-all.jar") 130 | } 131 | jarFile 132 | } 133 | 134 | static String getMainClass(Project project) { 135 | def mainClass = getRawMainClass(project) 136 | if(!mainClass) throw new GradleException("mainClass not configured") 137 | int pos = mainClass.lastIndexOf('/') 138 | if(pos < 0) return mainClass 139 | mainClass.substring(pos + 1) 140 | } 141 | 142 | @CompileDynamic 143 | static String getRawMainClass(Project project) { 144 | project.tasks.run.mainClass?.get() 145 | } 146 | 147 | @CompileDynamic 148 | static List getDefaultJvmArgs(Project project) { 149 | try { 150 | return new ArrayList(project.application?.applicationDefaultJvmArgs as List) 151 | } catch (Exception e) { 152 | return [] 153 | } 154 | } 155 | 156 | @CompileDynamic 157 | static List getDefaultArgs(Project project) { 158 | try { 159 | return project.tasks.run?.args 160 | } catch (Exception e) { 161 | return [] 162 | } 163 | } 164 | 165 | static String getDefaultToolchainJavaHome(Project project) { 166 | try { 167 | def defaultToolchain = project.extensions.getByType(JavaPluginExtension)?.toolchain 168 | if(!defaultToolchain) return null 169 | JavaToolchainService service = project.extensions.getByType(JavaToolchainService) 170 | def launcherProvider = service?.launcherFor(defaultToolchain) 171 | if(!launcherProvider?.present) return null 172 | return launcherProvider.get()?.metadata?.installationPath?.asFile?.absolutePath 173 | } catch(e) { 174 | project.logger.warn("Cannot retrieve Java toolchain: $e") 175 | return null 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/resources/unixScriptTemplate.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## ${applicationName} start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: \$0 may be a link 11 | PRG="\$0" 12 | # Need this for relative symlinks. 13 | while [ -h "\$PRG" ] ; do 14 | ls=`ls -ld "\$PRG"` 15 | link=`expr "\$ls" : '.*-> \\(.*\\)\$'` 16 | if expr "\$link" : '/.*' > /dev/null; then 17 | PRG="\$link" 18 | else 19 | PRG=`dirname "\$PRG"`"/\$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"\$PRG\"`/${appHomeRelativePath}" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "\$SAVED" >/dev/null 26 | 27 | APP_NAME="${applicationName}" 28 | APP_BASE_NAME=`basename "\$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS=${defaultJvmOpts} 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "\$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "\$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH="\$APP_HOME/lib/*" 68 | 69 | JAVA_HOME="\$APP_HOME" 70 | JAVACMD="\$JAVA_HOME/bin/java" 71 | 72 | # Increase the maximum file descriptors if we can. 73 | if [ "\$cygwin" = "false" -a "\$darwin" = "false" -a "\$nonstop" = "false" ] ; then 74 | MAX_FD_LIMIT=`ulimit -H -n` 75 | if [ \$? -eq 0 ] ; then 76 | if [ "\$MAX_FD" = "maximum" -o "\$MAX_FD" = "max" ] ; then 77 | MAX_FD="\$MAX_FD_LIMIT" 78 | fi 79 | ulimit -n \$MAX_FD 80 | if [ \$? -ne 0 ] ; then 81 | warn "Could not set maximum file descriptor limit: \$MAX_FD" 82 | fi 83 | else 84 | warn "Could not query maximum file descriptor limit: \$MAX_FD_LIMIT" 85 | fi 86 | fi 87 | 88 | # For Darwin, add options to specify how the application appears in the dock 89 | if \$darwin; then 90 | GRADLE_OPTS="\$GRADLE_OPTS \\"-Xdock:name=\$APP_NAME\\" \\"-Xdock:icon=\$APP_HOME/media/gradle.icns\\"" 91 | fi 92 | 93 | # For Cygwin, switch paths to Windows format before running java 94 | if \$cygwin ; then 95 | APP_HOME=`cygpath --path --mixed "\$APP_HOME"` 96 | CLASSPATH=`cygpath --path --mixed "\$CLASSPATH"` 97 | JAVA_HOME="\$APP_HOME" 98 | JAVACMD="\$JAVA_HOME/bin/java" 99 | 100 | # We build the pattern for arguments to be converted via cygpath 101 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 102 | SEP="" 103 | for dir in \$ROOTDIRSRAW ; do 104 | ROOTDIRS="\$ROOTDIRS\$SEP\$dir" 105 | SEP="|" 106 | done 107 | OURCYGPATTERN="(^(\$ROOTDIRS))" 108 | # Add a user-defined pattern to the cygpath arguments 109 | if [ "\$GRADLE_CYGPATTERN" != "" ] ; then 110 | OURCYGPATTERN="\$OURCYGPATTERN|(\$GRADLE_CYGPATTERN)" 111 | fi 112 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 113 | i=0 114 | for arg in "\$@" ; do 115 | CHECK=`echo "\$arg"|egrep -c "\$OURCYGPATTERN" -` 116 | CHECK2=`echo "\$arg"|egrep -c "^-"` ### Determine if an option 117 | 118 | if [ \$CHECK -ne 0 ] && [ \$CHECK2 -eq 0 ] ; then ### Added a condition 119 | eval `echo args\$i`=`cygpath --path --ignore --mixed "\$arg"` 120 | else 121 | eval `echo args\$i`="\"\$arg\"" 122 | fi 123 | i=\$((i+1)) 124 | done 125 | case \$i in 126 | (0) set -- ;; 127 | (1) set -- "\$args0" ;; 128 | (2) set -- "\$args0" "\$args1" ;; 129 | (3) set -- "\$args0" "\$args1" "\$args2" ;; 130 | (4) set -- "\$args0" "\$args1" "\$args2" "\$args3" ;; 131 | (5) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" ;; 132 | (6) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" ;; 133 | (7) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" ;; 134 | (8) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" ;; 135 | (9) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" "\$args8" ;; 136 | esac 137 | fi 138 | 139 | # Escape application args 140 | save () { 141 | for i do printf %s\\\\n "\$i" | sed "s/'/'\\\\\\\\''/g;1s/^/'/;\\\$s/\\\$/' \\\\\\\\/" ; done 142 | echo " " 143 | } 144 | APP_ARGS=\$(save "\$@") 145 | 146 | <% if ( System.properties['BADASS_CDS_ARCHIVE_FILE_LINUX'] ) { %> 147 | CDS_ARCHIVE_FILE="<%= System.properties['BADASS_CDS_ARCHIVE_FILE_LINUX'] %>" 148 | CDS_JVM_OPTS="-XX:ArchiveClassesAtExit=\$CDS_ARCHIVE_FILE" 149 | if [ -f "\$CDS_ARCHIVE_FILE" ]; then 150 | CDS_JVM_OPTS="-XX:SharedArchiveFile=\$CDS_ARCHIVE_FILE" 151 | fi 152 | <% } %> 153 | 154 | # Collect all arguments for the java command, following the shell quoting and substitution rules 155 | eval set -- \$DEFAULT_JVM_OPTS \$CDS_JVM_OPTS \$JAVA_OPTS \$${optsEnvironmentVar} <% if ( appNameSystemProperty ) { %>"\"-D${appNameSystemProperty}=\$APP_BASE_NAME\"" <% } %>-classpath "\"\$CLASSPATH\"" ${mainClassName} "\$APP_ARGS" 156 | 157 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 158 | if [ "\$(uname)" = "Darwin" ] && [ "\$HOME" = "\$PWD" ]; then 159 | cd "\$(dirname "\$0")" 160 | fi 161 | 162 | <% if ( System.properties['BADASS_RUN_IN_BIN_DIR'] ) { %>cd "\$APP_HOME/bin" && <% } %>exec "\$JAVACMD" "\$@" 163 | -------------------------------------------------------------------------------- /src/main/resources/windowsScriptTemplate.txt: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem ${applicationName} startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | 14 | set APP_BASE_NAME=%~n0 15 | set APP_HOME=%DIRNAME%${appHomeRelativePath} 16 | 17 | @rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. 18 | set DEFAULT_JVM_OPTS=${defaultJvmOpts} 19 | 20 | set JAVA_HOME="%APP_HOME%" 21 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 22 | set JAVA_EXE="%JAVA_EXE:"=%" 23 | 24 | if exist %JAVA_EXE% goto init 25 | 26 | echo. 27 | echo ERROR: The directory %JAVA_HOME% does not contain a valid Java runtime for your platform. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :init 35 | @rem Get command-line arguments, handling Windows variants 36 | 37 | if not "%OS%" == "Windows_NT" goto win9xME_args 38 | 39 | :win9xME_args 40 | @rem Slurp the command line arguments. 41 | set CMD_LINE_ARGS= 42 | set _SKIP=2 43 | 44 | :win9xME_args_slurp 45 | if "x%~1" == "x" goto execute 46 | 47 | set CMD_LINE_ARGS=%* 48 | 49 | :execute 50 | @rem Setup the command line 51 | 52 | set CLASSPATH="%JAVA_HOME:"=%/lib/*" 53 | 54 | <% if ( System.properties['BADASS_CDS_ARCHIVE_FILE_WINDOWS'] ) { %> 55 | set CDS_ARCHIVE_FILE="<%= System.properties['BADASS_CDS_ARCHIVE_FILE_WINDOWS'] %>" 56 | set CDS_JVM_OPTS=-XX:ArchiveClassesAtExit=%CDS_ARCHIVE_FILE% 57 | if exist %CDS_ARCHIVE_FILE% set CDS_JVM_OPTS=-XX:SharedArchiveFile=%CDS_ARCHIVE_FILE% 58 | <% } %> 59 | 60 | @rem Execute ${applicationName} 61 | <% if ( System.properties['BADASS_RUN_IN_BIN_DIR'] ) { %>pushd %DIRNAME% & <% } %>%JAVA_EXE% %DEFAULT_JVM_OPTS% %CDS_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath %CLASSPATH% ${mainClassName} %CMD_LINE_ARGS%<% if ( System.properties['BADASS_RUN_IN_BIN_DIR'] ) { %> & popd<% } %> 62 | 63 | :end 64 | @rem End local scope for the variables with windows NT shell 65 | if "%ERRORLEVEL%"=="0" goto mainEnd 66 | 67 | :fail 68 | rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of 69 | rem the _cmd.exe /c_ return code! 70 | if not "" == "%${exitEnvironmentVar}%" exit 1 71 | exit /b 1 72 | 73 | :mainEnd 74 | if "%OS%"=="Windows_NT" endlocal 75 | 76 | :omega 77 | -------------------------------------------------------------------------------- /src/main/resources/windowsScriptTemplateJavaw.txt: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem ${applicationName} startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | 14 | set APP_BASE_NAME=%~n0 15 | set APP_HOME=%DIRNAME%${appHomeRelativePath} 16 | 17 | @rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. 18 | set DEFAULT_JVM_OPTS=${defaultJvmOpts} 19 | 20 | set JAVA_HOME="%APP_HOME%" 21 | set JAVA_EXE=%JAVA_HOME%\\bin\\javaw.exe 22 | set JAVA_EXE="%JAVA_EXE:"=%" 23 | 24 | if exist %JAVA_EXE% goto init 25 | 26 | echo. 27 | echo ERROR: The directory %JAVA_HOME% does not contain a valid Java runtime for your platform. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :init 35 | @rem Get command-line arguments, handling Windows variants 36 | 37 | if not "%OS%" == "Windows_NT" goto win9xME_args 38 | 39 | :win9xME_args 40 | @rem Slurp the command line arguments. 41 | set CMD_LINE_ARGS= 42 | set _SKIP=2 43 | 44 | :win9xME_args_slurp 45 | if "x%~1" == "x" goto execute 46 | 47 | set CMD_LINE_ARGS=%* 48 | 49 | :execute 50 | @rem Setup the command line 51 | 52 | set CLASSPATH="%JAVA_HOME:"=%/lib/*" 53 | 54 | <% if ( System.properties['BADASS_CDS_ARCHIVE_FILE_WINDOWS'] ) { %> 55 | set CDS_ARCHIVE_FILE="<%= System.properties['BADASS_CDS_ARCHIVE_FILE_WINDOWS'] %>" 56 | set CDS_JVM_OPTS=-XX:ArchiveClassesAtExit=%CDS_ARCHIVE_FILE% 57 | if exist %CDS_ARCHIVE_FILE% set CDS_JVM_OPTS=-XX:SharedArchiveFile=%CDS_ARCHIVE_FILE% 58 | <% } %> 59 | 60 | @rem Execute ${applicationName} 61 | <% if ( System.properties['BADASS_RUN_IN_BIN_DIR'] ) { %>pushd %DIRNAME% & <% } %>start "${applicationName}" %JAVA_EXE% %DEFAULT_JVM_OPTS% %CDS_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath %CLASSPATH% ${mainClassName} %CMD_LINE_ARGS%<% if ( System.properties['BADASS_RUN_IN_BIN_DIR'] ) { %> & popd<% } %> 62 | 63 | :end 64 | @rem End local scope for the variables with windows NT shell 65 | if "%ERRORLEVEL%"=="0" goto mainEnd 66 | 67 | :fail 68 | rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of 69 | rem the _cmd.exe /c_ return code! 70 | if not "" == "%${exitEnvironmentVar}%" exit 1 71 | exit /b 1 72 | 73 | :mainEnd 74 | if "%OS%"=="Windows_NT" endlocal 75 | 76 | :omega 77 | -------------------------------------------------------------------------------- /src/test/groovy/org/beryx/runtime/ModuleManagerSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import org.beryx.runtime.util.ModuleManager 19 | import spock.lang.Specification 20 | 21 | class ModuleManagerSpec extends Specification { 22 | def "should correctly retrieve the names of the referenced packages"() { 23 | given: 24 | def moduleManager = new ModuleManager(System.properties['java.home']) 25 | 26 | when: 27 | def map = moduleManager.getExportsMap(new File("src/test/resources/libs").absolutePath) 28 | 29 | then: 30 | map['java.util.concurrent'] == 'java.base' 31 | map['javax.naming.directory'] == 'java.naming' 32 | map['org.xml.sax.helpers'] == 'java.xml' 33 | map['javax.xml.bind.annotation.adapters'] == 'java.xml.bind' 34 | map['ch.qos.logback.classic.net.server'] == 'ch.qos.logback.classic' 35 | map['ch.qos.logback.core.encoder'] == 'ch.qos.logback.core' 36 | map['org.slf4j.spi'] == 'org.slf4j' 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/groovy/org/beryx/runtime/PackageUseScannerSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import org.beryx.runtime.util.PackageUseScanner 19 | import spock.lang.Specification 20 | 21 | class PackageUseScannerSpec extends Specification { 22 | def "should correctly retrieve the names of the referenced packages"() { 23 | given: 24 | def scanner = new PackageUseScanner() 25 | def jarFile = new File('src/test/resources/libs/logback-core-1.3.0-alpha4.jar') 26 | 27 | when: 28 | def invalidEntries = scanner.scan(jarFile) 29 | if(invalidEntries) println "invalidEntries: $invalidEntries" 30 | 31 | then: 32 | !invalidEntries 33 | scanner.externalPackages == [ 34 | 'java.io', 35 | 'java.lang', 36 | 'java.lang.annotation', 37 | 'java.lang.reflect', 38 | 'java.net', 39 | 'java.nio.channels', 40 | 'java.nio.charset', 41 | 'java.nio.file', 42 | 'java.security', 43 | 'java.security.cert', 44 | 'java.sql', 45 | 'java.text', 46 | 'java.util', 47 | 'java.util.concurrent', 48 | 'java.util.concurrent.atomic', 49 | 'java.util.concurrent.locks', 50 | 'java.util.regex', 51 | 'java.util.zip', 52 | 'javax.mail', 53 | 'javax.mail.internet', 54 | 'javax.naming', 55 | 'javax.net', 56 | 'javax.net.ssl', 57 | 'javax.servlet', 58 | 'javax.servlet.http', 59 | 'javax.sql', 60 | 'javax.xml.namespace', 61 | 'javax.xml.parsers', 62 | 'javax.xml.stream', 63 | 'javax.xml.stream.events', 64 | 'org.codehaus.commons.compiler', 65 | 'org.codehaus.janino', 66 | 'org.xml.sax', 67 | 'org.xml.sax.helpers' 68 | ] as TreeSet 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/groovy/org/beryx/runtime/RuntimePluginSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import org.gradle.testkit.runner.BuildResult 19 | import org.gradle.testkit.runner.GradleRunner 20 | import spock.lang.Specification 21 | import spock.lang.TempDir 22 | import spock.lang.Unroll 23 | import spock.util.environment.OperatingSystem 24 | 25 | import java.nio.file.Path 26 | 27 | import static org.gradle.testkit.runner.TaskOutcome.* 28 | 29 | class RuntimePluginSpec extends Specification { 30 | @TempDir Path testProjectDir 31 | 32 | def cleanup() { 33 | println "CLEANUP" 34 | } 35 | 36 | def setUpBuild(Collection modules) { 37 | new AntBuilder().copy( todir: testProjectDir ) { 38 | fileset( dir: 'src/test/resources/hello-logback' ) 39 | } 40 | 41 | File buildFile = new File(testProjectDir.toFile(), "build.gradle") 42 | buildFile << ''' 43 | runtime { 44 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] 45 | '''.stripIndent() 46 | if(modules != null) { 47 | buildFile << " modules = [${modules.collect{'\'' + it + '\''}.join(', ')}]\n" 48 | } 49 | buildFile << '}\n' 50 | println "Executing build script:\n${buildFile.text}" 51 | } 52 | 53 | @Unroll 54 | def "if modules=#modules, then buildSucceeds=#buildShouldSucceed and runSucceeds=#runShouldSucceed with Gradle #gradleVersion"() { 55 | when: 56 | setUpBuild(modules) 57 | BuildResult result 58 | try { 59 | result = GradleRunner.create() 60 | .withDebug(true) 61 | .withProjectDir(testProjectDir.toFile()) 62 | .withGradleVersion(gradleVersion) 63 | .withPluginClasspath() 64 | .withArguments(RuntimePlugin.TASK_NAME_RUNTIME, "-is") 65 | .build(); 66 | } catch (Exception e) { 67 | if(buildShouldSucceed) { 68 | e.printStackTrace() 69 | } 70 | assert !buildShouldSucceed 71 | return 72 | } 73 | def imageBinDir = new File(testProjectDir.toFile(), 'build/image/bin') 74 | def launcherExt = OperatingSystem.current.windows ? '.bat' : '' 75 | def imageLauncher = new File(imageBinDir, "runtime-hello$launcherExt") 76 | 77 | then: 78 | result.task(":$RuntimePlugin.TASK_NAME_RUNTIME").outcome == SUCCESS 79 | imageLauncher.exists() 80 | 81 | when: 82 | imageLauncher.setExecutable(true) 83 | def process = imageLauncher.absolutePath.execute([], imageBinDir) 84 | def out = new ByteArrayOutputStream(2048) 85 | def err = new ByteArrayOutputStream(2048) 86 | process.waitForProcessOutput(out, err) 87 | def outputText = out.toString() 88 | 89 | then: 90 | (outputText.trim() == 'LOG: Hello, runtime!') == runShouldSucceed 91 | 92 | where: 93 | modules | buildShouldSucceed | runShouldSucceed | gradleVersion 94 | null | true | true | '7.6' 95 | [] | true | true | '7.6' 96 | ['java.base'] | true | false | '7.0' 97 | ['foo.bar'] | false | false | '7.6' 98 | ['java.naming'] | true | false | '7.0' 99 | ['java.naming', 'java.xml'] | true | true | '7.6' 100 | ['java.naming', 'java.xml', 'java.logging'] | true | true | '7.0' 101 | ['java.naming', 'java.xml', 'foo.bar'] | false | false | '7.6' 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/groovy/org/beryx/runtime/SuggestModulesSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.beryx.runtime 17 | 18 | import org.gradle.testkit.runner.BuildResult 19 | import org.gradle.testkit.runner.GradleRunner 20 | import org.gradle.testkit.runner.TaskOutcome 21 | import spock.lang.Specification 22 | import spock.lang.TempDir 23 | import spock.lang.Unroll 24 | 25 | import java.nio.file.Path 26 | import java.util.stream.Collectors 27 | 28 | class SuggestModulesSpec extends Specification { 29 | @TempDir Path testProjectDir 30 | 31 | def cleanup() { 32 | println "CLEANUP" 33 | } 34 | 35 | @Unroll 36 | def "should suggest the correct list of modules"() { 37 | given: 38 | new AntBuilder().copy( todir: testProjectDir ) { 39 | fileset( dir: 'src/test/resources/hello-log4j-2.9.0' ) 40 | } 41 | File buildFile = new File(testProjectDir.toFile(), "build.gradle") 42 | def outputWriter = new StringWriter(8192) 43 | 44 | when: 45 | BuildResult result = GradleRunner.create() 46 | .withDebug(true) 47 | .forwardStdOutput(outputWriter) 48 | .withProjectDir(buildFile.parentFile) 49 | .withPluginClasspath() 50 | .withArguments("-is", RuntimePlugin.TASK_NAME_SUGGEST_MODULES) 51 | .build(); 52 | def task = result.task(":$RuntimePlugin.TASK_NAME_SUGGEST_MODULES") 53 | println outputWriter 54 | 55 | then: 56 | task.outcome == TaskOutcome.SUCCESS 57 | 58 | when: 59 | def taskOutput = outputWriter.toString() 60 | def modules = getModules(taskOutput) 61 | 62 | then: 63 | modules as Set == [ 64 | 'java.sql', 65 | 'java.naming', 66 | 'java.desktop', 67 | 'java.rmi', 68 | 'java.logging', 69 | 'java.compiler', 70 | 'java.scripting', 71 | 'java.xml', 72 | 'java.management' 73 | ] as Set 74 | } 75 | 76 | static Set getModules(String taskOutput) { 77 | String blockStart = 'modules = [' 78 | String blockEnd = ']' 79 | int startPos = taskOutput.indexOf(blockStart) 80 | assert startPos >= 0 81 | startPos += blockStart.length() 82 | int endPos = taskOutput.indexOf(blockEnd, startPos) 83 | assert endPos >= 0 84 | def content = taskOutput.substring(startPos, endPos) 85 | content.lines() 86 | .map{it.trim()} 87 | .map{it.replaceAll("[',]", "")} 88 | .filter{!it.empty} 89 | .collect(Collectors.toList()) as Set 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/resources/hello-log4j-2.9.0/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.beryx.runtime' 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | sourceCompatibility = 11 10 | targetCompatibility = 11 11 | 12 | ext.log4jVersion = '2.9.0' 13 | 14 | dependencies { 15 | implementation "org.apache.logging.log4j:log4j-api:$log4jVersion" 16 | implementation "org.apache.logging.log4j:log4j-core:$log4jVersion" 17 | implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion" 18 | implementation "javax.xml.bind:jaxb-api:2.3.0" 19 | } 20 | 21 | application { 22 | mainClass = 'org.example.runtime.hello.Hello' 23 | } 24 | 25 | jar { 26 | manifest { 27 | attributes 'Implementation-Title': "hello", 28 | 'Main-Class': 'org.example.runtime.hello.Hello' 29 | } 30 | } 31 | 32 | tasks.runtime.doLast { 33 | copy { 34 | from('src/main/resources') 35 | into("$buildDir/image/bin") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/resources/hello-log4j-2.9.0/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'runtime-hello' 2 | -------------------------------------------------------------------------------- /src/test/resources/hello-log4j-2.9.0/src/main/java/org/example/runtime/hello/Hello.java: -------------------------------------------------------------------------------- 1 | package org.example.runtime.hello; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class Hello { 7 | private static final Logger logger = LoggerFactory.getLogger(Hello.class); 8 | 9 | public static void main(String[] args) { 10 | logger.info("Hello, runtime!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/hello-log4j-2.9.0/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/test/resources/hello-logback/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.beryx.runtime' 3 | id 'com.github.johnrengelman.shadow' version '7.1.2' 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | sourceCompatibility = 8 11 | targetCompatibility = 8 12 | 13 | dependencies { 14 | implementation 'org.slf4j:slf4j-api:1.7.25' 15 | implementation 'ch.qos.logback:logback-classic:1.2.3' 16 | implementation 'javax.xml.bind:jaxb-api:2.3.0' 17 | } 18 | 19 | application { 20 | mainClass = 'org.example.runtime.Hello' 21 | } 22 | 23 | jar { 24 | manifest { 25 | attributes 'Implementation-Title': "runtime-hello", 26 | 'Main-Class': 'org.example.runtime.Hello' 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/hello-logback/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'runtime-hello' 2 | -------------------------------------------------------------------------------- /src/test/resources/hello-logback/src/main/java/org/example/runtime/Hello.java: -------------------------------------------------------------------------------- 1 | package org.example.runtime; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class Hello { 7 | private static final Logger logger = LoggerFactory.getLogger(Hello.class); 8 | 9 | public static void main(String[] args) { 10 | logger.info("Hello, runtime!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/hello-logback/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LOG: %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/test/resources/libs/ecj-3.13.102.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/ecj-3.13.102.jar -------------------------------------------------------------------------------- /src/test/resources/libs/java.base.jmod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/java.base.jmod -------------------------------------------------------------------------------- /src/test/resources/libs/java.naming.jmod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/java.naming.jmod -------------------------------------------------------------------------------- /src/test/resources/libs/java.xml.jmod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/java.xml.jmod -------------------------------------------------------------------------------- /src/test/resources/libs/jaxb-api-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/jaxb-api-2.3.0.jar -------------------------------------------------------------------------------- /src/test/resources/libs/logback-classic-1.3.0-alpha4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/logback-classic-1.3.0-alpha4.jar -------------------------------------------------------------------------------- /src/test/resources/libs/logback-core-1.3.0-alpha4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/logback-core-1.3.0-alpha4.jar -------------------------------------------------------------------------------- /src/test/resources/libs/multi-release-hello.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/multi-release-hello.jar -------------------------------------------------------------------------------- /src/test/resources/libs/slf4j-api-1.8.0-beta2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beryx/badass-runtime-plugin/c32d653b2242ad88f11aba6572227a2ed58486e4/src/test/resources/libs/slf4j-api-1.8.0-beta2.jar --------------------------------------------------------------------------------