├── .github ├── release-drafter.yml ├── scripts │ ├── update-ide-properties.sh │ └── update-next-development.sh └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main └── java └── com └── gluonhq └── gradle ├── ClientExtension.java ├── GluonFXPlugin.java ├── ReleaseConfiguration.java ├── attach ├── AttachConfiguration.java ├── AttachService.java └── AttachServiceDefinition.java └── tasks ├── ConfigBuild.java ├── NativeBaseTask.java ├── NativeBuildTask.java ├── NativeCompileTask.java ├── NativeInstallTask.java ├── NativeLinkTask.java ├── NativePackageTask.java ├── NativeRunAgentTask.java └── NativeRunTask.java /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | exclude-labels: 2 | - 'housekeeping' 3 | template: | 4 | ## What’s Changed 5 | $CHANGES -------------------------------------------------------------------------------- /.github/scripts/update-ide-properties.sh: -------------------------------------------------------------------------------- 1 | # Update settings property file 2 | aws s3 cp s3://download.gluonhq.com/ideplugins/settings-2.10.4.properties /tmp --region us-east-1 --debug 3 | sed -i "s/gluonfxGradlePlugin=.*/gluonfxGradlePlugin=$1/g" /tmp/settings-2.10.4.properties 4 | aws s3 cp /tmp/settings-2.10.4.properties s3://download.gluonhq.com/ideplugins/ --acl public-read --region us-east-1 --debug -------------------------------------------------------------------------------- /.github/scripts/update-next-development.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REPO_SLUG=gluonhq/gluonfx-gradle-plugin 4 | # Assign parameter to variable 5 | TAG=$1 6 | 7 | # Update version by 1 8 | newVersion=${TAG%.*}.$((${TAG##*.} + 1)) 9 | 10 | echo "Update README with the latest released version" 11 | sed -i "0,/id 'com.gluonhq.gluonfx-gradle-plugin' version '.*'/s//id 'com.gluonhq.gluonfx-gradle-plugin' version '$TAG'/" README.md 12 | sed -i "0,/'com.gluonhq:gluonfx-gradle-plugin:.*'/s//'com.gluonhq:gluonfx-gradle-plugin:$TAG'/" README.md 13 | git commit README.md -m "Use latest release v$TAG in README" 14 | 15 | # Replace first occurrence of 16 | # version 'TAG' 17 | # with 18 | # version 'newVersion-SNAPSHOT' 19 | sed -i -z "0,/\nversion '$TAG'/s//\nversion '$newVersion-SNAPSHOT'/" build.gradle 20 | 21 | # Find substrate version 22 | substrateVersion=$(grep com.gluonhq:substrate: build.gradle | tr -d "'" | cut -d \: -f 3) 23 | # Update version by 1 24 | newSubstrateVersion=${substrateVersion%.*}.$((${substrateVersion##*.} + 1)) 25 | echo "Update Substrate version" 26 | sed -i -z "0,/com.gluonhq:substrate:$substrateVersion/s//com.gluonhq:substrate:$newSubstrateVersion-SNAPSHOT/" build.gradle 27 | 28 | git commit build.gradle -m "Prepare development of $newVersion" 29 | git push https://gluon-bot:$GITHUB_PASSWORD@github.com/$REPO_SLUG HEAD:master -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Setup Java 11 and Apache Maven 18 | uses: actions/setup-java@v4 19 | with: 20 | distribution: 'temurin' 21 | java-version: 11 22 | 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | 26 | - name: Build project 27 | run: | 28 | ./gradlew -i build 29 | 30 | - name: Publish Snapshots 31 | if: github.ref == 'refs/heads/master' 32 | run: | 33 | ver=$(./gradlew properties -q | grep "version:" | awk '{print $2}') 34 | if [[ $ver == *"SNAPSHOT"* ]] 35 | then 36 | ./gradlew publish -PsonatypeUsername=$SONATYPE_USERNAME -PsonatypePassword=$SONATYPE_PASSWORD 37 | fi 38 | shell: bash 39 | env: 40 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 41 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 42 | 43 | - name: Draft release 44 | if: github.ref == 'refs/heads/master' 45 | # Drafts your next Release notes as Pull Requests are merged into "master" 46 | uses: release-drafter/release-drafter@v5 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 5 15 | persist-credentials: false 16 | 17 | - name: Setup Java 11 and Apache Maven 18 | uses: actions/setup-java@v4 19 | with: 20 | distribution: 'temurin' 21 | java-version: 11 22 | 23 | - name: Configure GIT 24 | run: | 25 | git config --global user.name "Gluon Bot" 26 | git config --global user.email "githubbot@gluonhq.com" 27 | 28 | - name: Publish to Gradle Plugin Repository 29 | run: | 30 | ./gradlew publishPlugins -Pgradle.publish.key=$PUBLISH_KEY -Pgradle.publish.secret=$PUBLISH_SECRET 31 | env: 32 | PUBLISH_KEY: ${{ secrets.PUBLISH_KEY }} 33 | PUBLISH_SECRET: ${{ secrets.PUBLISH_SECRET }} 34 | 35 | - name: Commit next development version 36 | if: steps.deploy.outputs.exit_code == 0 37 | run: | 38 | TAG=${GITHUB_REF/refs\/tags\//} 39 | bash $GITHUB_WORKSPACE/.github/scripts/update-next-development.sh "$TAG" 40 | shell: bash 41 | env: 42 | GITHUB_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 43 | 44 | - name: Update ide-plugin properties 45 | if: steps.deploy.outputs.exit_code == 0 46 | run: | 47 | TAG=${GITHUB_REF/refs\/tags\//} 48 | echo "Update ide-plugin properties" 49 | bash $GITHUB_WORKSPACE/.github/scripts/update-ide-properties.sh "$TAG" 50 | shell: bash 51 | env: 52 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 53 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .gradle 25 | build 26 | .idea 27 | /.classpath 28 | /.project 29 | /.settings/ 30 | /bin/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Gluon 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GluonFX plugin for Gradle 2 | 3 | [![Plugin Portal](https://img.shields.io/maven-metadata/v?label=Gradle%20Plugin%20Portal&metadataUrl=https://plugins.gradle.org/m2/com/gluonhq/gluonfx-gradle-plugin/maven-metadata.xml)](https://plugins.gradle.org/plugin/com.gluonhq.gluonfx-gradle-plugin) 4 | [![Build](https://github.com/gluonhq/gluonfx-gradle-plugin/actions/workflows/build.yml/badge.svg)](https://github.com/gluonhq/gluonfx-gradle-plugin/actions/workflows/build.yml) 5 | [![BSD-3 license](https://img.shields.io/badge/license-BSD--3-%230778B9.svg)](https://opensource.org/licenses/BSD-3-Clause) 6 | 7 | GluonFX plugin for gradle projects leverages GraalVM, OpenJDK and JavaFX 11+, 8 | by compiling into native code the Java Client application and all its required dependencies, 9 | so it can directly be executed as a native application on the target platform. 10 | 11 | # Important Notice 12 | 13 | Gluon releases the [GluonFX plugin for Maven](https://github.com/gluonhq/gluonfx-maven-plugin), and this plugin is maintained and kept up to date by the community. 14 | 15 | Use at your own risk. 16 | 17 | ## Getting started 18 | 19 | To use the plugin, apply the following steps: 20 | 21 | ### 1. Apply the plugin 22 | 23 | Using the `plugins` DSL, add: 24 | 25 | plugins { 26 | id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.26' 27 | } 28 | 29 | This requires adding the plugin repository to the `settings.gradle` file: 30 | 31 | pluginManagement { 32 | repositories { 33 | gradlePluginPortal() 34 | } 35 | } 36 | rootProject.name = ... 37 | 38 | Alternatively, you can use the `buildscript` DSL: 39 | 40 | buildscript { 41 | repositories { 42 | maven { 43 | url "https://plugins.gradle.org/m2/" 44 | } 45 | } 46 | dependencies { 47 | classpath 'com.gluonhq:gluonfx-gradle-plugin:1.0.26' 48 | } 49 | } 50 | apply plugin: 'com.gluonhq.gluonfx-gradle-plugin' 51 | 52 | 53 | ### 2. Tasks 54 | 55 | You can run the regular tasks to build and run your project as a regular VM project: 56 | 57 | ./gradlew clean build 58 | ./gradlew run 59 | 60 | Once the project is ready, the plugin has these main tasks: 61 | 62 | #### `nativeRunAgent` 63 | 64 | This task can be run to use a tracing agent and generate the required config files for native-image. 65 | 66 | Run: 67 | 68 | ./gradlew nativeRunAgent 69 | 70 | #### `nativeCompile` 71 | 72 | This tasks does the AOT compilation. It is a very intensive and lengthy task (several minutes, depending on your project and CPU), 73 | so it should be called only when the project is ready and runs fine on a VM. 74 | 75 | Run: 76 | 77 | ./gradlew build nativeCompile 78 | 79 | The results will be available at `$buildDir/client/gvm`. 80 | 81 | #### `nativeLink` 82 | 83 | When the object is created, this task will generate the native executable for the target platform. 84 | 85 | Run: 86 | 87 | ./gradlew nativeLink 88 | 89 | The results will be available at `$buildDir/client/$hostPlatform/$AppName`. 90 | 91 | #### `nativeBuild` 92 | 93 | This task simply combines `nativeCompile` and `nativeLink`. 94 | 95 | #### `nativeRun` 96 | 97 | Runs the executable in the target platform 98 | 99 | Run: 100 | 101 | ./gradlew nativeRun 102 | 103 | Or run the three tasks combined: 104 | 105 | ./gradlew build nativeBuild nativeRun 106 | 107 | Or run directly the application from command line: 108 | 109 | build/gluonfx/$hostPlatform/$AppName/$AppName 110 | 111 | It will create a distributable native application. 112 | 113 | #### `nativePackage` 114 | 115 | On mobile only, create a package of the executable in the target platform 116 | 117 | Run: 118 | 119 | ./gradlew nativePackage 120 | 121 | On iOS, this can be used to create an IPA, on Android it will create an APK. 122 | 123 | #### `nativeInstall` 124 | 125 | Installs the generated package or the binary. 126 | 127 | Run: 128 | 129 | ./gradlew nativeInstall 130 | 131 | Note: At the moment, this task is only intended for Android and Linux-AArch64. 132 | 133 | ### Configuration 134 | 135 | The plugin allows some configuration to modify the default settings: 136 | 137 | ``` 138 | gluonfx { 139 | target = "$target" 140 | attachConfig { 141 | version = "$version" 142 | configuration = "implementation"; 143 | services "lifecycle", ... 144 | } 145 | 146 | bundlesList = [] 147 | resourcesList = [] 148 | reflectionList = [] 149 | jniList = [] 150 | 151 | compilerArgs = [] 152 | linkerArgs = [] 153 | runtimeArgs = [] 154 | 155 | javaStaticSdkVersion = "" 156 | javafxStaticSdkVersion = "" 157 | graalvmHome = "" 158 | 159 | verbose = false 160 | enableSwRendering = false 161 | 162 | remoteHostName = "" 163 | remoteDir = "" 164 | 165 | release { 166 | // Android 167 | appLabel = "" 168 | versionCode = "1" 169 | versionName = "1.0" 170 | providedKeyStorePath = "" 171 | providedKeyStorePassword = "" 172 | providedKeyAlias = "" 173 | providedKeyAliasPassword = "" 174 | // iOS 175 | bundleName = "" 176 | bundleVersion = "" 177 | bundleShortVersion = "" 178 | providedSigningIdentity = "" 179 | providedProvisioningProfile = "" 180 | skipSigning = false 181 | } 182 | } 183 | ``` 184 | 185 | Check the [maven counterpart section](https://docs.gluonhq.com/#_configuration) for more details. 186 | 187 | ### Requirements 188 | 189 | Check the requirements for the [target platform](https://docs.gluonhq.com/#_platforms) before you get started. 190 | 191 | ## Issues and Contributions ## 192 | 193 | Issues can be reported to the [Issue tracker](https://github.com/gluonhq/gluonfx-gradle-plugin/issues) 194 | 195 | Contributions can be submitted via [Pull requests](https://github.com/gluonhq/gluonfx-gradle-plugin/pulls), 196 | providing you have signed the [Gluon Individual Contributor License Agreement (CLA)](https://cla.gluonhq.com). 197 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.gradle.plugin-publish' version '1.2.1' 3 | id 'com.github.ben-manes.versions' version '0.47.0' 4 | id 'com.github.hierynomus.license' version '0.16.1' 5 | } 6 | 7 | group 'com.gluonhq' 8 | version '1.0.27-SNAPSHOT' 9 | 10 | java { 11 | toolchain.languageVersion = JavaLanguageVersion.of(11) 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | gradlePluginPortal() 17 | maven { 18 | url "https://oss.sonatype.org/content/repositories/snapshots" 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation gradleApi() 24 | 25 | testImplementation gradleTestKit() 26 | implementation 'com.gluonhq:substrate:0.0.67-SNAPSHOT' 27 | implementation 'org.openjfx:javafx-plugin:0.1.0' 28 | } 29 | 30 | gradlePlugin { 31 | plugins { 32 | gluonFXPlugin { 33 | id = 'com.gluonhq.gluonfx-gradle-plugin' 34 | displayName = 'GluonFX Plugin' 35 | description = 'GluonFX plugin allows to run JavaFX application on the JVM or to create their native images.' 36 | implementationClass = 'com.gluonhq.gradle.GluonFXPlugin' 37 | website = 'https://github.com/gluonhq/gluonfx-gradle-plugin' 38 | vcsUrl = 'https://github.com/gluonhq/gluonfx-gradle-plugin' 39 | tags.set([ 'java', 'javafx', 'gluon', 'client', 'substrate', 'graalvm', 'aot' ]) 40 | } 41 | } 42 | } 43 | 44 | publishing { 45 | repositories { 46 | maven { 47 | url = "https://oss.sonatype.org/content/repositories/snapshots/" 48 | def sonatypeUsername = providers.gradleProperty('sonatypeUsername') 49 | def sonatypePassword = providers.gradleProperty('sonatypePassword') 50 | if (sonatypeUsername.isPresent() && sonatypePassword.isPresent()) { 51 | credentials { 52 | username = sonatypeUsername.get() 53 | password = sonatypePassword.get() 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | license { 61 | skipExistingHeaders = true 62 | mapping { 63 | java = 'SLASHSTAR_STYLE' 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gluonhq/gluonfx-gradle-plugin/36e24be8de59dc13b2365a886b80bfbec7307930/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip 7 | networkTimeout=10000 8 | validateDistributionUrl=true -------------------------------------------------------------------------------- /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 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /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 init 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 init 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 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | 7 | plugins { 8 | id 'com.gradle.enterprise' version '3.14.1' 9 | } 10 | 11 | gradleEnterprise { 12 | buildScan { 13 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 14 | termsOfServiceAgree = 'yes' 15 | } 16 | } 17 | 18 | rootProject.name = 'gluonfx-gradle-plugin' 19 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/ClientExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2022, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | import org.gradle.api.Project; 36 | import org.gradle.api.model.ObjectFactory; 37 | import org.gradle.util.ConfigureUtil; 38 | 39 | import com.gluonhq.gradle.attach.AttachConfiguration; 40 | 41 | import groovy.lang.Closure; 42 | 43 | public class ClientExtension { 44 | 45 | private static final String DEFAULT_TARGET = "host"; 46 | 47 | /** 48 | * Defines the target platform. Default is host, which refers to the platform 49 | * that currently hosts the process (macosx or linux), but could be also set to 50 | * ios (either simulator or device), if the host is a Mac. 51 | * Default is "host" 52 | */ 53 | private String target; 54 | 55 | /** 56 | * List of additional full qualified bundle resources that will be added to 57 | * the default bundles list, that already includes: 58 | * - "com/sun/javafx/scene/control/skin/resources/controls", 59 | * - "com.sun.javafx.tk.quantum.QuantumMessagesBundle" 60 | */ 61 | private final List bundlesList; 62 | 63 | /** 64 | * List of additional resource patterns or extensions that will be added 65 | * to the default resource list that already includes: 66 | * - png, gif, jpg, jpeg, bmp, 67 | * - ttf, css, fxml, json 68 | * - frag, gls, license 69 | */ 70 | private final List resourcesList; 71 | 72 | /** 73 | * List of additional full qualified classes that will be added to the default 74 | * reflection list, that already includes most of the JavaFX classes. 75 | */ 76 | private final List reflectionList; 77 | 78 | /** 79 | * List of additional full qualified classes that will be added to the default 80 | * jni list, that already includes most of the JavaFX classes. 81 | */ 82 | private final List jniList; 83 | 84 | /** 85 | * List of optional compiler arguments 86 | */ 87 | private final List compilerArgs; 88 | 89 | /** 90 | * List of optional linker arguments 91 | */ 92 | private final List linkerArgs; 93 | 94 | /** 95 | * List of optional runtime arguments 96 | */ 97 | private final List runtimeArgs; 98 | 99 | /** 100 | * The Java static SDK version 101 | */ 102 | private String javaStaticSdkVersion; 103 | 104 | /** 105 | * The JavaFX static SDK version 106 | */ 107 | private String javafxStaticSdkVersion; 108 | 109 | /** 110 | * The GraalVM Home directory 111 | */ 112 | private String graalvmHome; 113 | 114 | /** 115 | * Enables verbose output 116 | * By default is false 117 | */ 118 | private boolean verbose; 119 | 120 | /** 121 | * Enables software rendering. 122 | * By default is false 123 | */ 124 | private boolean enableSwRendering; 125 | 126 | /** 127 | * host name for remote deploying, typically to an 128 | * embedded system, providing it is reachable and SSH is 129 | * enabled 130 | */ 131 | private String remoteHostName; 132 | 133 | /** 134 | * Sets the directory where the native image will be 135 | * deployed on the remote system, providing the remote 136 | * host is reachable and SSH is enabled. 137 | */ 138 | private String remoteDir; 139 | 140 | /** 141 | * Sets a unique application identifier. 142 | */ 143 | private String appIdentifier; 144 | 145 | private AttachConfiguration attachConfiguration; 146 | 147 | private ReleaseConfiguration releaseConfiguration; 148 | 149 | public ClientExtension(Project project, ObjectFactory objectFactory) { 150 | this.target = DEFAULT_TARGET; 151 | this.bundlesList = new ArrayList<>(); 152 | this.resourcesList = new ArrayList<>(); 153 | this.reflectionList = new ArrayList<>(); 154 | this.jniList = new ArrayList<>(); 155 | this.compilerArgs = new ArrayList<>(); 156 | this.linkerArgs = new ArrayList<>(); 157 | this.runtimeArgs = new ArrayList<>(); 158 | 159 | attachConfiguration = objectFactory.newInstance(AttachConfiguration.class, project); 160 | releaseConfiguration = objectFactory.newInstance(ReleaseConfiguration.class, project); 161 | } 162 | 163 | public String getGraalvmHome() { 164 | return graalvmHome; 165 | } 166 | 167 | public void setGraalvmHome(String graalvmHome) { 168 | this.graalvmHome = graalvmHome; 169 | } 170 | 171 | public String getJavaStaticSdkVersion() { 172 | return javaStaticSdkVersion; 173 | } 174 | 175 | public void setJavaStaticSdkVersion(String javaStaticSdkVersion) { 176 | this.javaStaticSdkVersion = javaStaticSdkVersion; 177 | } 178 | 179 | public String getJavafxStaticSdkVersion() { 180 | return javafxStaticSdkVersion; 181 | } 182 | 183 | public void setJavafxStaticSdkVersion(String javafxStaticSdkVersion) { 184 | this.javafxStaticSdkVersion = javafxStaticSdkVersion; 185 | } 186 | 187 | public String getTarget() { 188 | return target; 189 | } 190 | 191 | public void setTarget(String target) { 192 | this.target = target; 193 | } 194 | 195 | public List getBundlesList() { 196 | return bundlesList; 197 | } 198 | 199 | public void setBundlesList(List bundlesList) { 200 | this.bundlesList.clear(); 201 | this.bundlesList.addAll(bundlesList); 202 | } 203 | 204 | public List getResourcesList() { 205 | return resourcesList; 206 | } 207 | 208 | public void setResourcesList(List resourcesList) { 209 | this.resourcesList.clear(); 210 | this.resourcesList.addAll(resourcesList); 211 | } 212 | 213 | public List getReflectionList() { 214 | return reflectionList; 215 | } 216 | 217 | public void setReflectionList(List reflectionList) { 218 | this.reflectionList.clear(); 219 | this.reflectionList.addAll(reflectionList); 220 | } 221 | 222 | public void setJniList(List jniList) { 223 | this.jniList.clear(); 224 | this.jniList.addAll(jniList); 225 | } 226 | 227 | public List getJniList() { 228 | return jniList; 229 | } 230 | 231 | public void setCompilerArgs(List compilerArgs) { 232 | this.compilerArgs.clear(); 233 | this.compilerArgs.addAll(compilerArgs); 234 | } 235 | 236 | public List getCompilerArgs() { 237 | return compilerArgs; 238 | } 239 | 240 | public void setLinkerArgs(List linkerArgs) { 241 | this.linkerArgs.clear(); 242 | this.linkerArgs.addAll(linkerArgs); 243 | } 244 | 245 | public List getLinkerArgs() { 246 | return linkerArgs; 247 | } 248 | 249 | public void setRuntimeArgs(List compilerArgs) { 250 | this.runtimeArgs.clear(); 251 | this.runtimeArgs.addAll(compilerArgs); 252 | } 253 | 254 | public List getRuntimeArgs() { 255 | return runtimeArgs; 256 | } 257 | 258 | public boolean isVerbose() { 259 | return verbose; 260 | } 261 | 262 | public void setVerbose(boolean verbose) { 263 | this.verbose = verbose; 264 | } 265 | 266 | public boolean isEnableSwRendering() { 267 | return enableSwRendering; 268 | } 269 | 270 | public void setEnableSwRendering(boolean enableSwRendering) { 271 | this.enableSwRendering = enableSwRendering; 272 | } 273 | 274 | public void setRemoteHostName(String remoteHostName) { 275 | this.remoteHostName = remoteHostName; 276 | } 277 | 278 | public String getRemoteHostName() { 279 | return remoteHostName; 280 | } 281 | 282 | public void setRemoteDir(String remoteDir) { 283 | this.remoteDir = remoteDir; 284 | } 285 | 286 | public String getRemoteDir() { 287 | return remoteDir; 288 | } 289 | 290 | public void setAppIdentifier(String appIdentifier) { 291 | this.appIdentifier = appIdentifier; 292 | } 293 | 294 | public String getAppIdentifier() { 295 | return appIdentifier; 296 | } 297 | 298 | public void attachConfig(Closure configureClosure) { 299 | ConfigureUtil.configure(configureClosure, attachConfiguration); 300 | } 301 | 302 | public AttachConfiguration getAttachConfig() { 303 | return attachConfiguration; 304 | } 305 | 306 | public void release(Closure configureClosure) { 307 | ConfigureUtil.configure(configureClosure, releaseConfiguration); 308 | } 309 | 310 | public ReleaseConfiguration getReleaseConfiguration() { 311 | return releaseConfiguration; 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/GluonFXPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle; 31 | 32 | import com.gluonhq.gradle.tasks.NativeBuildTask; 33 | import com.gluonhq.gradle.tasks.NativeCompileTask; 34 | import com.gluonhq.gradle.tasks.NativeInstallTask; 35 | import com.gluonhq.gradle.tasks.NativeLinkTask; 36 | import com.gluonhq.gradle.tasks.NativePackageTask; 37 | import com.gluonhq.gradle.tasks.NativeRunTask; 38 | import com.gluonhq.gradle.tasks.NativeRunAgentTask; 39 | import org.gradle.api.Plugin; 40 | import org.gradle.api.Project; 41 | import org.gradle.api.Task; 42 | import org.gradle.api.model.ObjectFactory; 43 | 44 | import javax.inject.Inject; 45 | 46 | public class GluonFXPlugin implements Plugin { 47 | 48 | public static final String NATIVE_COMPILE_TASK_NAME = "nativeCompile"; 49 | public static final String NATIVE_LINK_TASK_NAME = "nativeLink"; 50 | public static final String NATIVE_RUN_TASK_NAME = "nativeRun"; 51 | public static final String NATIVE_BUILD_TASK_NAME = "nativeBuild"; 52 | public static final String NATIVE_PACKAGE_TASK_NAME = "nativePackage"; 53 | public static final String NATIVE_INSTALL_TASK_NAME = "nativeInstall"; 54 | public static final String NATIVE_RUN_AGENT_TASK_NAME = "nativeRunAgent"; 55 | 56 | private static final String CONFIGURATION_CLIENT = "client"; 57 | 58 | private ObjectFactory objectFactory; 59 | private Project project; 60 | 61 | @Inject 62 | GluonFXPlugin(ObjectFactory objectFactory) { 63 | this.objectFactory = objectFactory; 64 | } 65 | 66 | @Override 67 | public void apply(Project project) { 68 | this.project = project; 69 | 70 | project.getConfigurations().create(CONFIGURATION_CLIENT); 71 | 72 | project.getExtensions().create("gluonfx", ClientExtension.class, project, objectFactory); 73 | 74 | createTask(NATIVE_COMPILE_TASK_NAME, NativeCompileTask.class, "Native AOT compilation of application."); 75 | createTask(NATIVE_LINK_TASK_NAME, NativeLinkTask.class, "Native link of application."); 76 | createTask(NATIVE_BUILD_TASK_NAME, NativeBuildTask.class, "Combines AOT compilation and link of application."); 77 | createTask(NATIVE_RUN_TASK_NAME, NativeRunTask.class, "Runs the native application in the target platform."); 78 | createTask(NATIVE_PACKAGE_TASK_NAME, NativePackageTask.class, "Packages the native application for the target platform."); 79 | createTask(NATIVE_INSTALL_TASK_NAME, NativeInstallTask.class, "Installs the packaged native application on the target platform."); 80 | createTask(NATIVE_RUN_AGENT_TASK_NAME, NativeRunAgentTask.class, "Runs tracing agent to generate config files"); 81 | } 82 | 83 | private void createTask(String name, Class taskClass, String description) { 84 | Task t = project.getTasks().create(name, taskClass, project); 85 | t.setGroup("GluonFX"); 86 | t.setDescription(description); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/ReleaseConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD 3-Clause License 3 | * 4 | * Copyright (c) 2020, 2022, Gluon Software 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * * Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package com.gluonhq.gradle; 33 | 34 | import org.gradle.api.Project; 35 | 36 | import javax.inject.Inject; 37 | 38 | public class ReleaseConfiguration { 39 | 40 | private static final String DEFAULT_DESCRIPTION = "Default description"; 41 | private static final String DEFAULT_VENDOR = "Unknown"; 42 | private static final String DEFAULT_VERSION = "1.0"; 43 | 44 | public static final String DEFAULT_MAC_APP_CATEGORY = "public.app-category.utilities"; 45 | public static final String DEFAULT_BUNDLE_VERSION = "1.0"; 46 | private static final String DEFAULT_BUNDLE_SHORT_VERSION = "1.0"; 47 | 48 | private static final String DEFAULT_VERSION_CODE = "1"; 49 | 50 | private final Project project; 51 | 52 | /** 53 | * Type of package bundle that can be generated. 54 | * 55 | * - On macOS, 'pkg' or 'dmg' can be selected. Note that 'app' is generated by default. 56 | * 57 | * Note that on iOS 'app' and 'ipa', and Android 'apk' and 'aab', are already generated by default 58 | */ 59 | private String packageType; 60 | 61 | /** 62 | * A short description about the application 63 | * 64 | * Default: Empty string. 65 | */ 66 | private String description; 67 | 68 | /** 69 | * Vendor of the application. 70 | * Ideally, name of the company or individual developing the application. 71 | */ 72 | private String vendor; 73 | 74 | /** 75 | * A string used as the version number shown to users, like 76 | * .. 77 | * 78 | * Default: 1.0 79 | */ 80 | private String version; 81 | 82 | // macOS 83 | 84 | /** 85 | * Boolean that indicates if the macOS bundle is intended for the Mac App Store. 86 | */ 87 | private boolean macAppStore; 88 | 89 | /** 90 | * Team or user name portion in Apple signing identities 91 | */ 92 | private String macSigningUserName; 93 | 94 | /** 95 | * The category that best describes the app for the Mac App Store. 96 | * Default is public.app-category.utilities. See 97 | * https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype 98 | * for the full list of categories. 99 | */ 100 | private String macAppCategory; 101 | 102 | // macOS/iOS 103 | 104 | /** 105 | * A user-visible short name for the bundle 106 | * 107 | * Default: if not set, $appName will be used. 108 | */ 109 | private String bundleName; 110 | 111 | /** 112 | * The version of the build that identifies an iteration of the bundle. A 113 | * string composed of one to three period-separated integers, containing 114 | * numeric characters (0-9) and periods only. 115 | * 116 | * Default: 1.0 117 | */ 118 | private String bundleVersion; 119 | 120 | /** 121 | * A user-visible string for the release or version number of the bundle. A 122 | * string composed of one to three period-separated integers, containing 123 | * numeric characters (0-9) and periods only. 124 | * 125 | * Default: 1.0 126 | */ 127 | private String bundleShortVersion; 128 | 129 | /** 130 | * String that identifies a valid certificate that will be used for macOS/iOS development 131 | * or macOS/iOS distribution. 132 | * 133 | * Default: null. When not provided, Substrate will be selected from all the valid identities found 134 | * installed on the machine from any of these types: 135 | * 136 | * macOS: Apple Development|Apple Distribution|Mac Developer|3rd Party Mac Developer Application|Developer ID Application 137 | * iOS: iPhone Developer|Apple Development|iOS Development|iPhone Distribution 138 | * 139 | * and that were used by the provisioning profile. 140 | */ 141 | private String providedSigningIdentity; 142 | 143 | /** 144 | * String with the name of the provisioning profile created for macOS/iOS development or 145 | * distribution of the given app. 146 | * 147 | * Default: null. When not provided, Substrate will try to find a valid installed 148 | * provisioning profile that can be used to sign the app, including wildcards. 149 | */ 150 | private String providedProvisioningProfile; 151 | 152 | /** 153 | * Boolean that can be used to skip signing macOS/iOS apps. This will prevent any 154 | * deployment, but can be useful to run tests without an actual device 155 | */ 156 | private boolean skipSigning; 157 | 158 | /** 159 | * A string with a valid name of an iOS simulator device 160 | */ 161 | private String simulatorDevice; 162 | 163 | // Android 164 | 165 | /** 166 | * A user-visible short name for the app 167 | * 168 | * Default: if not set, $appName will be used. 169 | */ 170 | private String appLabel; 171 | 172 | /** 173 | * A positive integer used as an internal version number 174 | * 175 | * Default: 1 176 | */ 177 | private String versionCode; 178 | 179 | /** 180 | * A string used as the version number shown to users, like 181 | * .. 182 | * 183 | * Default: 1.0 184 | */ 185 | private String versionName; 186 | 187 | /** 188 | * A string with the path to a keystore file that can be used to sign 189 | * the Android apk. 190 | * 191 | * Default: null. If not set, Substrate creates and uses a debug keystore. 192 | */ 193 | private String providedKeyStorePath; 194 | 195 | /** 196 | * A string with the password of the provide keystore file. 197 | * 198 | * Default: null. If not set, Substrate creates and uses a debug keystore. 199 | */ 200 | private String providedKeyStorePassword; 201 | 202 | /** 203 | * A string with an identifying name for the key 204 | * 205 | * Default: null. If not set, Substrate creates and uses a debug keystore. 206 | */ 207 | private String providedKeyAlias; 208 | 209 | /** 210 | * A string with a password for the key 211 | * 212 | * Default: null. If not set, Substrate creates and uses a debug keystore. 213 | */ 214 | private String providedKeyAliasPassword; 215 | 216 | @Inject 217 | public ReleaseConfiguration(Project project) { 218 | this.project = project; 219 | } 220 | 221 | public void setPackageType(String packageType) { 222 | this.packageType = packageType; 223 | } 224 | 225 | public String getPackageType() { 226 | return packageType; 227 | } 228 | 229 | public String getDescription() { 230 | return description == null ? "" : description; 231 | } 232 | 233 | public void setDescription(String description) { 234 | this.description = description; 235 | } 236 | 237 | public String getVendor() { 238 | return vendor; 239 | } 240 | 241 | public void setVendor(String vendor) { 242 | this.vendor = vendor; 243 | } 244 | 245 | public String getVersion() { 246 | return version; 247 | } 248 | 249 | public void setVersion(String version) { 250 | this.version = version; 251 | } 252 | 253 | public boolean isMacAppStore() { 254 | return macAppStore; 255 | } 256 | 257 | public void setMacAppStore(boolean macAppStore) { 258 | this.macAppStore = macAppStore; 259 | } 260 | 261 | public String getMacSigningUserName() { 262 | return macSigningUserName; 263 | } 264 | 265 | public void setMacSigningUserName(String macSigningUserName) { 266 | this.macSigningUserName = macSigningUserName; 267 | } 268 | 269 | public void setMacAppCategory(String macAppCategory) { 270 | this.macAppCategory = macAppCategory; 271 | } 272 | 273 | public String getMacAppCategory() { 274 | return macAppCategory; 275 | } 276 | 277 | public String getBundleName() { 278 | return bundleName; 279 | } 280 | 281 | public void setBundleName(String bundleName) { 282 | this.bundleName = bundleName; 283 | } 284 | 285 | public String getBundleVersion() { 286 | return bundleVersion; 287 | } 288 | 289 | public void setBundleVersion(String bundleVersion) { 290 | this.bundleVersion = bundleVersion; 291 | } 292 | 293 | public String getBundleShortVersion() { 294 | return bundleShortVersion; 295 | } 296 | 297 | public void setBundleShortVersion(String bundleShortVersion) { 298 | this.bundleShortVersion = bundleShortVersion; 299 | } 300 | 301 | public String getProvidedSigningIdentity() { 302 | return providedSigningIdentity; 303 | } 304 | 305 | public void setProvidedSigningIdentity(String providedSigningIdentity) { 306 | this.providedSigningIdentity = providedSigningIdentity; 307 | } 308 | 309 | public String getProvidedProvisioningProfile() { 310 | return providedProvisioningProfile; 311 | } 312 | 313 | public void setProvidedProvisioningProfile(String providedProvisioningProfile) { 314 | this.providedProvisioningProfile = providedProvisioningProfile; 315 | } 316 | 317 | public boolean isSkipSigning() { 318 | return skipSigning; 319 | } 320 | 321 | public void setSkipSigning(boolean skipSigning) { 322 | this.skipSigning = skipSigning; 323 | } 324 | 325 | public String getSimulatorDevice() { 326 | return simulatorDevice; 327 | } 328 | 329 | public void setSimulatorDevice(String simulatorDevice) { 330 | this.simulatorDevice = simulatorDevice; 331 | } 332 | 333 | public String getAppLabel() { 334 | return appLabel; 335 | } 336 | 337 | public void setAppLabel(String appLabel) { 338 | this.appLabel = appLabel; 339 | } 340 | 341 | public String getVersionCode() { 342 | return versionCode; 343 | } 344 | 345 | public void setVersionCode(String versionCode) { 346 | this.versionCode = versionCode; 347 | } 348 | 349 | public String getVersionName() { 350 | return versionName; 351 | } 352 | 353 | public void setVersionName(String versionName) { 354 | this.versionName = versionName; 355 | } 356 | 357 | public String getProvidedKeyStorePath() { 358 | return providedKeyStorePath; 359 | } 360 | 361 | public void setProvidedKeyStorePath(String providedKeyStorePath) { 362 | this.providedKeyStorePath = providedKeyStorePath; 363 | } 364 | 365 | public String getProvidedKeyStorePassword() { 366 | return providedKeyStorePassword; 367 | } 368 | 369 | public void setProvidedKeyStorePassword(String providedKeyStorePassword) { 370 | this.providedKeyStorePassword = providedKeyStorePassword; 371 | } 372 | 373 | public String getProvidedKeyAlias() { 374 | return providedKeyAlias; 375 | } 376 | 377 | public void setProvidedKeyAlias(String providedKeyAlias) { 378 | this.providedKeyAlias = providedKeyAlias; 379 | } 380 | 381 | public String getProvidedKeyAliasPassword() { 382 | return providedKeyAliasPassword; 383 | } 384 | 385 | public void setProvidedKeyAliasPassword(String providedKeyAliasPassword) { 386 | this.providedKeyAliasPassword = providedKeyAliasPassword; 387 | } 388 | 389 | @Override 390 | public String toString() { 391 | return "ReleaseConfiguration{" + 392 | "packageType=" + packageType + 393 | ", description='" + description + '\'' + 394 | ", vendor='" + vendor + '\'' + 395 | ", version='" + version + '\'' + 396 | ", macAppStore=" + macAppStore + 397 | ", macSigningUserName=" + macSigningUserName + 398 | ", macAppCategory=" + macAppCategory + 399 | ", bundleName='" + bundleName + '\'' + 400 | ", bundleVersion='" + bundleVersion + '\'' + 401 | ", bundleShortVersion='" + bundleShortVersion + '\'' + 402 | ", providedSigningIdentity='" + providedSigningIdentity + '\'' + 403 | ", providedProvisioningProfile='" + providedProvisioningProfile + '\'' + 404 | ", skipSigning=" + skipSigning + 405 | ", simulatorDevice='" + simulatorDevice + '\'' + 406 | ", appLabel='" + appLabel + '\'' + 407 | ", versionCode='" + versionCode + '\'' + 408 | ", versionName='" + versionName + '\'' + 409 | ", providedKeyStorePath='" + providedKeyStorePath + '\'' + 410 | ", providedKeyStorePassword='" + providedKeyStorePassword + '\'' + 411 | ", providedKeyAlias='" + providedKeyAlias + '\'' + 412 | ", providedKeyAliasPassword='" + providedKeyAliasPassword + '\'' + 413 | '}'; 414 | } 415 | 416 | public com.gluonhq.substrate.model.ReleaseConfiguration toSubstrate() { 417 | com.gluonhq.substrate.model.ReleaseConfiguration release = new com.gluonhq.substrate.model.ReleaseConfiguration(); 418 | 419 | release.setPackageType(getPackageType()); 420 | release.setDescription(getDescription()); 421 | release.setVendor(getVendor()); 422 | release.setVersion(getVersion()); 423 | // macOS 424 | release.setMacAppStore(isMacAppStore()); 425 | release.setMacSigningUserName(getMacSigningUserName()); 426 | release.setMacAppCategory(getMacAppCategory()); 427 | // macOS/iOS 428 | release.setBundleName(getBundleName()); 429 | release.setBundleVersion(getBundleVersion()); 430 | release.setBundleShortVersion(getBundleShortVersion()); 431 | release.setProvidedSigningIdentity(getProvidedSigningIdentity()); 432 | release.setProvidedProvisioningProfile(getProvidedProvisioningProfile()); 433 | release.setSkipSigning(isSkipSigning()); 434 | release.setSimulatorDevice(getSimulatorDevice()); 435 | // Android 436 | release.setAppLabel(getAppLabel()); 437 | release.setVersionCode(getVersionCode()); 438 | release.setVersionName(getVersionName()); 439 | release.setProvidedKeyStorePath(getProvidedKeyStorePath()); 440 | release.setProvidedKeyStorePassword(getProvidedKeyStorePassword()); 441 | release.setProvidedKeyAlias(getProvidedKeyAlias()); 442 | release.setProvidedKeyAliasPassword(getProvidedKeyAliasPassword()); 443 | 444 | return release; 445 | } 446 | } 447 | 448 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/attach/AttachConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD 3-Clause License 3 | * 4 | * Copyright (c) 2018, 2023, Gluon Software 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * * Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package com.gluonhq.gradle.attach; 33 | 34 | import java.util.Collection; 35 | import java.util.HashMap; 36 | import java.util.Map; 37 | 38 | import javax.inject.Inject; 39 | 40 | import org.gradle.api.Action; 41 | import org.gradle.api.NamedDomainObjectContainer; 42 | import org.gradle.api.Project; 43 | import org.gradle.api.artifacts.Configuration; 44 | 45 | import com.gluonhq.gradle.ClientExtension; 46 | import com.gluonhq.substrate.Constants; 47 | import org.gradle.api.artifacts.ModuleDependency; 48 | 49 | public class AttachConfiguration { 50 | private static final String DEPENDENCY_GROUP = "com.gluonhq.attach"; 51 | private static final String UTIL_ARTIFACT = "util"; 52 | 53 | private Project project; 54 | 55 | private String version; 56 | private String configuration = "implementation"; 57 | 58 | private NamedDomainObjectContainer services; 59 | private Configuration lastAppliedConfiguration; 60 | 61 | @Inject 62 | public AttachConfiguration(Project project) { 63 | this.project = project; 64 | this.services = project.container(AttachServiceDefinition.class); 65 | } 66 | 67 | public void version(String version) { 68 | this.version = version; 69 | } 70 | 71 | public String getVersion() { 72 | return version; 73 | } 74 | 75 | public void setVersion(String version) { 76 | this.version = version; 77 | } 78 | 79 | public void setConfiguration(String configuration) { 80 | this.configuration = configuration; 81 | applyConfiguration(); 82 | } 83 | 84 | public String getConfiguration() { 85 | return configuration; 86 | } 87 | public Collection getServices() { 88 | return services; 89 | } 90 | 91 | public void services(String... services) { 92 | if (services != null) { 93 | for (String service : services) { 94 | this.services.create(service); 95 | } 96 | applyConfiguration(); 97 | } 98 | } 99 | 100 | /** 101 | * Configures attach services. 102 | * @param action action parameter 103 | */ 104 | public void services(Action> action) { 105 | action.execute(services); 106 | } 107 | 108 | /** 109 | * Add dependencies to the specified configuration. Only dependencies to services that support the provided 110 | * configuration will be included. 111 | */ 112 | private void applyConfiguration() { 113 | if (version == null) { 114 | throw new IllegalStateException("Attach version must be specified!"); 115 | } 116 | 117 | if (lastAppliedConfiguration != null) { 118 | lastAppliedConfiguration.getDependencies() 119 | .removeIf(dependency -> DEPENDENCY_GROUP.equals(dependency.getGroup())); 120 | } 121 | 122 | String configName = getConfiguration(); 123 | Configuration configuration = project.getConfigurations().getByName(configName); 124 | String target = project.getExtensions().getByType(ClientExtension.class).getTarget(); 125 | 126 | project.getLogger().info("Adding Attach dependencies for target: " + target); 127 | if (services != null && !services.isEmpty()) { 128 | services.stream() 129 | .map(asd -> generateDependencyNotation(asd, target)) 130 | .forEach(depNotion -> { 131 | ModuleDependency dep = (ModuleDependency) project.getDependencies().add(configName, depNotion); 132 | if (dep != null) { 133 | dep.exclude(Map.of("group", "org.openjfx", "module", "*")); 134 | } 135 | }); 136 | 137 | // Also add util artifact if any other artifact added 138 | Map utilDependencyNotationMap = new HashMap<>(); 139 | utilDependencyNotationMap.put("group", DEPENDENCY_GROUP); 140 | utilDependencyNotationMap.put("name", UTIL_ARTIFACT); 141 | utilDependencyNotationMap.put("version", getVersion()); 142 | if (Constants.PROFILE_ANDROID.equals(target) || Constants.PROFILE_IOS.equals(target) 143 | || Constants.PROFILE_IOS_SIM.equals(target)) { 144 | String utilTarget = Constants.PROFILE_IOS_SIM.equals(target) ? 145 | Constants.PROFILE_IOS : target; 146 | utilDependencyNotationMap.put("classifier", utilTarget); 147 | } 148 | ModuleDependency dep = (ModuleDependency) project.getDependencies().add(configName, utilDependencyNotationMap); 149 | if (dep != null) { 150 | dep.exclude(Map.of("group", "org.openjfx", "module", "*")); 151 | } 152 | } 153 | 154 | lastAppliedConfiguration = configuration; 155 | } 156 | 157 | private Map generateDependencyNotation(AttachServiceDefinition asd, String target) { 158 | Map dependencyNotationMap = new HashMap<>(); 159 | dependencyNotationMap.put("group", DEPENDENCY_GROUP); 160 | dependencyNotationMap.put("name", asd.getName()); 161 | dependencyNotationMap.put("version", getVersion()); 162 | dependencyNotationMap.put("classifier", asd.getSupportedPlatform(target)); 163 | 164 | project.getLogger().info("Adding dependency for {} in configuration {}: {}", asd.getService().getServiceName(), getConfiguration(), dependencyNotationMap); 165 | return dependencyNotationMap; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/attach/AttachService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2022, Gluon 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | package com.gluonhq.gradle.attach; 29 | 30 | import java.util.Locale; 31 | 32 | /** 33 | * List of all available services in Attach 34 | * See https://github.com/gluonhq/attach 35 | * 36 | * Each service has an iOS implementation, and some of them 37 | * a desktop implementation as well 38 | */ 39 | public enum AttachService { 40 | ACCELEROMETER, 41 | AUDIO, 42 | AUDIO_RECORDING(true /* desktopSupported */), 43 | AUGMENTED_REALITY, 44 | BARCODE_SCAN, 45 | BATTERY, 46 | BLE, 47 | BROWSER(true /* desktopSupported */), 48 | CACHE(true /* desktopSupported */), 49 | COMPASS, 50 | CONNECTIVITY, 51 | DEVICE, 52 | DIALER, 53 | DISPLAY(true /* desktopSupported */), 54 | IN_APP_BILLING, 55 | KEYBOARD, 56 | LIFECYCLE(true /* desktopSupported */), 57 | LOCAL_NOTIFICATIONS, 58 | MAGNETOMETER, 59 | ORIENTATION, 60 | PICTURES, 61 | POSITION, 62 | PUSH_NOTIFICATIONS, 63 | RUNTIME_ARGS(true /* desktopSupported */), 64 | SETTINGS(true /* desktopSupported */), 65 | SHARE, 66 | STATUSBAR, 67 | STORAGE(true /* desktopSupported */), 68 | STORE_REVIEW, 69 | VERSION(true /* desktopSupported */), 70 | VIBRATION, 71 | VIDEO; 72 | 73 | private boolean androidSupported = true; 74 | private boolean iosSupported = true; 75 | private boolean desktopSupported = false; 76 | 77 | AttachService() { } 78 | 79 | AttachService(boolean desktopSupported) { 80 | this.desktopSupported = desktopSupported; 81 | } 82 | 83 | public String getServiceName() { 84 | return name().replace('_', '-').toLowerCase(Locale.ROOT); 85 | } 86 | 87 | public boolean isAndroidSupported() { 88 | return androidSupported; 89 | } 90 | 91 | public boolean isIosSupported() { 92 | return iosSupported; 93 | } 94 | 95 | public boolean isDesktopSupported() { 96 | return desktopSupported; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/attach/AttachServiceDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD 3-Clause License 3 | * 4 | * Copyright (c) 2018, 2021, Gluon Software 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * * Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package com.gluonhq.gradle.attach; 33 | 34 | import com.gluonhq.substrate.Constants; 35 | import org.gradle.api.GradleException; 36 | import org.gradle.api.Named; 37 | import org.gradle.api.logging.LogLevel; 38 | import org.gradle.api.logging.Logger; 39 | import org.gradle.api.logging.Logging; 40 | 41 | import java.util.Locale; 42 | import java.util.stream.Collectors; 43 | import java.util.stream.Stream; 44 | 45 | public class AttachServiceDefinition implements Named { 46 | 47 | private static final Logger LOGGER = Logging.getLogger(AttachServiceDefinition.class.getName()); 48 | 49 | private String name; 50 | private AttachService service; 51 | 52 | private String version; 53 | 54 | public AttachServiceDefinition(String name) { 55 | this.name = name; 56 | 57 | try { 58 | this.service = AttachService.valueOf(name.replace('-', '_').toUpperCase(Locale.ROOT)); 59 | } catch (IllegalArgumentException e) { 60 | String services = Stream.of(AttachService.values()) 61 | .map(AttachService::getServiceName) 62 | .collect(Collectors.joining(", ")); 63 | LOGGER.log(LogLevel.ERROR, "Could not determine Attach service for name '" + name + "'. The following services are available: " + services); 64 | throw new GradleException("Invalid name for Attach service: " + name, e); 65 | } 66 | } 67 | 68 | @Override 69 | public String getName() { 70 | return name; 71 | } 72 | 73 | public AttachService getService() { 74 | return service; 75 | } 76 | 77 | public String getVersion() { 78 | return version; 79 | } 80 | 81 | public void version(String version) { 82 | this.version = version; 83 | } 84 | 85 | public void setVersion(String version) { 86 | version(version); 87 | } 88 | 89 | String getSupportedPlatform(String target) { 90 | switch (target) { 91 | case Constants.PROFILE_HOST: 92 | case Constants.PROFILE_LINUX_AARCH64: 93 | return getService().isDesktopSupported() ? "desktop" : ""; 94 | case Constants.PROFILE_IOS: 95 | case Constants.PROFILE_IOS_SIM: 96 | return getService().isIosSupported() ? "ios" : ""; 97 | case Constants.PROFILE_ANDROID: 98 | return getService().isAndroidSupported() ? "android" : ""; 99 | default: 100 | throw new RuntimeException("No valid target found for " + target); 101 | } 102 | } 103 | 104 | @Override 105 | public boolean equals(Object o) { 106 | if (this == o) return true; 107 | if (o == null || getClass() != o.getClass()) return false; 108 | 109 | AttachServiceDefinition that = (AttachServiceDefinition) o; 110 | 111 | return name.equals(that.name); 112 | 113 | } 114 | 115 | @Override 116 | public int hashCode() { 117 | return name.hashCode(); 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/ConfigBuild.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2024, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import java.io.File; 33 | import java.io.IOException; 34 | import java.nio.file.Path; 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | import java.util.Locale; 38 | import java.util.stream.Collectors; 39 | 40 | import org.gradle.api.GradleException; 41 | import org.gradle.api.Project; 42 | import org.gradle.api.artifacts.DependencySet; 43 | import org.gradle.api.plugins.ApplicationPlugin; 44 | import org.gradle.api.plugins.JavaApplication; 45 | import org.gradle.api.plugins.JavaPlugin; 46 | import org.gradle.api.provider.Property; 47 | import org.gradle.api.tasks.SourceSet; 48 | import org.gradle.api.tasks.SourceSetContainer; 49 | 50 | import com.gluonhq.gradle.ClientExtension; 51 | import com.gluonhq.substrate.Constants; 52 | import com.gluonhq.substrate.ProjectConfiguration; 53 | import com.gluonhq.substrate.SubstrateDispatcher; 54 | import com.gluonhq.substrate.model.Triplet; 55 | 56 | class ConfigBuild { 57 | 58 | private final Project project; 59 | private final ClientExtension clientExtension; 60 | 61 | ConfigBuild(Project project) { 62 | this.project = project; 63 | 64 | clientExtension = project.getExtensions().getByType(ClientExtension.class); 65 | } 66 | 67 | public SubstrateDispatcher createSubstrateDispatcher() throws IOException { 68 | Path clientPath = project.getLayout().getBuildDirectory().dir(Constants.GLUONFX_PATH).get().getAsFile().toPath(); 69 | project.getLogger().debug(" in directory {}", clientPath); 70 | 71 | return new SubstrateDispatcher(clientPath, createSubstrateConfiguration()); 72 | } 73 | 74 | public void build() { 75 | ProjectConfiguration clientConfig = createSubstrateConfiguration(); 76 | 77 | boolean result; 78 | try { 79 | String mainClassName = clientConfig.getMainClassName(); 80 | String name = clientConfig.getAppName(); 81 | for (org.gradle.api.artifacts.Configuration configuration : project.getBuildscript().getConfigurations()) { 82 | project.getLogger().debug("Configuration = " + configuration); 83 | DependencySet deps = configuration.getAllDependencies(); 84 | project.getLogger().debug("Dependencies = " + deps); 85 | deps.forEach(dep -> project.getLogger().debug("Dependency = " + dep)); 86 | } 87 | project.getLogger().debug("mainClassName = " + mainClassName + " and app name = " + name); 88 | 89 | Path buildRootPath = project.getLayout().getBuildDirectory().dir(Constants.GLUONFX_PATH).get().getAsFile().toPath(); 90 | project.getLogger().debug("BuildRoot: " + buildRootPath); 91 | 92 | SubstrateDispatcher dispatcher = new SubstrateDispatcher(buildRootPath, clientConfig); 93 | result = dispatcher.nativeCompile(); 94 | } catch (Exception e) { 95 | throw new GradleException("Failed to compile", e); 96 | } 97 | 98 | if (!result) { 99 | throw new IllegalStateException("Compilation failed"); 100 | } 101 | } 102 | 103 | private ProjectConfiguration createSubstrateConfiguration() { 104 | // Use Application Plugin First to get mainClass 105 | Property mainClass = project.getObjects().property(String.class); 106 | project.getPlugins().withType(ApplicationPlugin.class, applicationPlugin -> { 107 | JavaApplication javaApp = project.getExtensions().getByType(JavaApplication.class); 108 | mainClass.set(javaApp.getMainClass().getOrNull()); 109 | }); 110 | // Fallback to deprecated mainClassName 111 | if (!mainClass.isPresent()) { 112 | mainClass.set((String) project.getProperties().get("mainClassName")); 113 | } 114 | 115 | // Init Client Config 116 | ProjectConfiguration clientConfig = new ProjectConfiguration(mainClass.getOrNull(), getClassPath()); 117 | clientConfig.setJavaStaticSdkVersion(clientExtension.getJavaStaticSdkVersion()); 118 | clientConfig.setJavafxStaticSdkVersion(clientExtension.getJavafxStaticSdkVersion()); 119 | 120 | Triplet targetTriplet; 121 | String target = clientExtension.getTarget().toLowerCase(Locale.ROOT); 122 | switch (target) { 123 | case Constants.PROFILE_HOST: 124 | targetTriplet = Triplet.fromCurrentOS(); 125 | break; 126 | case Constants.PROFILE_IOS: 127 | targetTriplet = new Triplet(Constants.Profile.IOS); 128 | break; 129 | case Constants.PROFILE_IOS_SIM: 130 | targetTriplet = new Triplet(Constants.Profile.IOS_SIM); 131 | break; 132 | case Constants.PROFILE_ANDROID: 133 | targetTriplet = new Triplet(Constants.Profile.ANDROID); 134 | break; 135 | case Constants.PROFILE_LINUX_AARCH64: 136 | targetTriplet = new Triplet(Constants.Profile.LINUX_AARCH64); 137 | break; 138 | default: 139 | throw new RuntimeException("No valid target found for " + target); 140 | } 141 | clientConfig.setTarget(targetTriplet); 142 | 143 | clientConfig.setBundlesList(clientExtension.getBundlesList()); 144 | clientConfig.setResourcesList(clientExtension.getResourcesList()); 145 | clientConfig.setJniList(clientExtension.getJniList()); 146 | clientConfig.setCompilerArgs(clientExtension.getCompilerArgs()); 147 | clientConfig.setLinkerArgs(clientExtension.getLinkerArgs()); 148 | clientConfig.setRuntimeArgs(clientExtension.getRuntimeArgs()); 149 | clientConfig.setReflectionList(clientExtension.getReflectionList()); 150 | String appId = clientExtension.getAppIdentifier(); 151 | clientConfig.setAppId(appId != null ? appId : 152 | project.getGroup() + "." + project.getName()); 153 | clientConfig.setAppName(project.getName()); 154 | 155 | clientConfig.setGraalPath(getGraalHome()); 156 | 157 | clientConfig.setUsePrismSW(clientExtension.isEnableSwRendering()); 158 | clientConfig.setVerbose(clientExtension.isVerbose()); 159 | 160 | clientConfig.setRemoteHostName(clientExtension.getRemoteHostName()); 161 | clientConfig.setRemoteDir(clientExtension.getRemoteDir()); 162 | 163 | clientConfig.setReleaseConfiguration(clientExtension.getReleaseConfiguration().toSubstrate()); 164 | 165 | return clientConfig; 166 | } 167 | 168 | private String getClassPath() { 169 | List classPath = getClassPathFromSourceSets(); 170 | project.getLogger().debug("Runtime classPath = " + classPath); 171 | String cp = classPath.stream() 172 | .map(Path::toString) 173 | .collect(Collectors.joining(File.pathSeparator)) + File.pathSeparator; 174 | return cp; 175 | } 176 | 177 | private List getClassPathFromSourceSets() { 178 | final List classPath = new ArrayList<>(); 179 | project.getPlugins().withType(JavaPlugin.class, javaPlugin -> { 180 | SourceSetContainer sourceSetContainer = project.getExtensions().getByType(SourceSetContainer.class); 181 | SourceSet mainSourceSet = sourceSetContainer.getByName(SourceSet.MAIN_SOURCE_SET_NAME); 182 | mainSourceSet.getRuntimeClasspath().getFiles().stream() 183 | .filter(File::exists) 184 | .map(File::toPath) 185 | .forEachOrdered(classPath::add); 186 | }); 187 | return classPath; 188 | } 189 | 190 | private Path getGraalHome() { 191 | String graalvmHome = clientExtension.getGraalvmHome(); 192 | if (graalvmHome == null) { 193 | graalvmHome = System.getenv("GRAALVM_HOME"); 194 | } 195 | if (graalvmHome == null) { 196 | throw new GradleException("GraalVM installation directory not found." + 197 | " Either set GRAALVM_HOME as an environment variable or" + 198 | " set graalvmHome in the gluonfx-plugin configuration"); 199 | } 200 | return Path.of(graalvmHome); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeBaseTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import org.gradle.api.DefaultTask; 33 | import org.gradle.api.Project; 34 | import org.gradle.api.plugins.JavaPlugin; 35 | 36 | import javax.inject.Inject; 37 | 38 | abstract class NativeBaseTask extends DefaultTask { 39 | 40 | final Project project; 41 | 42 | @Inject 43 | public NativeBaseTask(Project project) { 44 | this.project = project; 45 | project.getPluginManager().withPlugin("java", e -> 46 | dependsOn(project.getTasks().findByName(JavaPlugin.CLASSES_TASK_NAME), 47 | project.getTasks().findByName(JavaPlugin.PROCESS_RESOURCES_TASK_NAME))); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeBuildTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import org.gradle.api.DefaultTask; 33 | import org.gradle.api.Project; 34 | import org.gradle.api.tasks.TaskAction; 35 | 36 | import javax.inject.Inject; 37 | 38 | public class NativeBuildTask extends DefaultTask { 39 | 40 | @Inject 41 | public NativeBuildTask(Project project) { 42 | dependsOn(project.getTasks().findByName("nativeCompile"), project.getTasks().findByName("nativeLink")); 43 | } 44 | 45 | @TaskAction 46 | public void action() { 47 | getProject().getLogger().info("ClientNativeBuild action"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeCompileTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import org.gradle.api.Project; 33 | import org.gradle.api.tasks.TaskAction; 34 | 35 | import javax.inject.Inject; 36 | 37 | public class NativeCompileTask extends NativeBaseTask { 38 | 39 | @Inject 40 | public NativeCompileTask(Project project) { 41 | super(project); 42 | } 43 | 44 | @TaskAction 45 | public void action() { 46 | getProject().getLogger().debug("ClientNativeCompile action"); 47 | new ConfigBuild(project).build(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeInstallTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import javax.inject.Inject; 33 | 34 | import org.gradle.api.GradleException; 35 | import org.gradle.api.Project; 36 | import org.gradle.api.tasks.TaskAction; 37 | 38 | import com.gluonhq.substrate.SubstrateDispatcher; 39 | 40 | public class NativeInstallTask extends NativeBaseTask { 41 | @Inject 42 | public NativeInstallTask(Project project) { 43 | super(project); 44 | } 45 | 46 | @TaskAction 47 | public void action() { 48 | getProject().getLogger().info("ClientNativeInstall action"); 49 | 50 | boolean result; 51 | try { 52 | SubstrateDispatcher dispatcher = new ConfigBuild(project).createSubstrateDispatcher(); 53 | result = dispatcher.nativeInstall(); 54 | } catch (Exception e) { 55 | throw new GradleException("Failed to install", e); 56 | } 57 | 58 | if (!result) { 59 | throw new GradleException("Installing failed"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeLinkTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import javax.inject.Inject; 33 | 34 | import org.gradle.api.GradleException; 35 | import org.gradle.api.Project; 36 | import org.gradle.api.tasks.TaskAction; 37 | 38 | import com.gluonhq.substrate.SubstrateDispatcher; 39 | 40 | public class NativeLinkTask extends NativeBaseTask { 41 | @Inject 42 | public NativeLinkTask(Project project) { 43 | super(project); 44 | } 45 | 46 | @TaskAction 47 | public void action() { 48 | getProject().getLogger().info("ClientNativeLink action"); 49 | 50 | boolean result; 51 | try { 52 | SubstrateDispatcher dispatcher = new ConfigBuild(project).createSubstrateDispatcher(); 53 | result = dispatcher.nativeLink(); 54 | } catch (Exception e) { 55 | throw new GradleException("Failed to link", e); 56 | } 57 | 58 | if (!result) { 59 | throw new GradleException("Linking failed"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativePackageTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import javax.inject.Inject; 33 | 34 | import org.gradle.api.GradleException; 35 | import org.gradle.api.Project; 36 | import org.gradle.api.tasks.TaskAction; 37 | 38 | import com.gluonhq.substrate.SubstrateDispatcher; 39 | 40 | public class NativePackageTask extends NativeBaseTask { 41 | @Inject 42 | public NativePackageTask(Project project) { 43 | super(project); 44 | } 45 | 46 | @TaskAction 47 | public void action() { 48 | getProject().getLogger().info("ClientNativePackage action"); 49 | 50 | boolean result; 51 | try { 52 | SubstrateDispatcher dispatcher = new ConfigBuild(project).createSubstrateDispatcher(); 53 | result = dispatcher.nativePackage(); 54 | } catch (Exception e) { 55 | throw new GradleException("Failed to package", e); 56 | } 57 | 58 | if (!result) { 59 | throw new GradleException("Packaging failed"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeRunAgentTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, 2024, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import com.gluonhq.gradle.ClientExtension; 33 | import org.gradle.api.GradleException; 34 | import org.gradle.api.Project; 35 | import org.gradle.api.Task; 36 | import org.gradle.api.plugins.ApplicationPlugin; 37 | import org.gradle.api.tasks.JavaExec; 38 | import org.gradle.api.tasks.TaskAction; 39 | 40 | import javax.inject.Inject; 41 | import java.io.BufferedWriter; 42 | import java.io.File; 43 | import java.io.FileOutputStream; 44 | import java.io.IOException; 45 | import java.io.OutputStreamWriter; 46 | import java.nio.file.Files; 47 | import java.nio.file.Path; 48 | import java.util.ArrayList; 49 | import java.util.Arrays; 50 | import java.util.List; 51 | import java.util.Locale; 52 | 53 | public class NativeRunAgentTask extends NativeBaseTask { 54 | 55 | private static final String AGENTLIB_NATIVE_IMAGE_AGENT_STRING = 56 | "-agentlib:native-image-agent=access-filter-file=src/main/resources/META-INF/native-image/filter-file.json,config-merge-dir=src/main/resources/META-INF/native-image"; 57 | 58 | private static final List AGENTLIB_EXCLUSION_RULES = Arrays.asList( 59 | "com.sun.glass.ui.mac.*", "com.sun.glass.ui.gtk.*", "com.sun.glass.ui.win.*", 60 | "com.sun.prism.es2.*", "com.sun.prism.d3d.*", 61 | "com.sun.scenario.effect.impl.es2.*", "com.sun.scenario.effect.impl.hw.d3d.*", 62 | "com.gluonhq.attach.**" 63 | ); 64 | 65 | private final ClientExtension clientExtension; 66 | 67 | @Inject 68 | public NativeRunAgentTask(Project project) { 69 | super(project); 70 | clientExtension = project.getExtensions().getByType(ClientExtension.class); 71 | } 72 | 73 | @TaskAction 74 | public void action() { 75 | getProject().getLogger().info("ClientNativeRunAgent action"); 76 | 77 | Path graalVMHome = getGraalHome(); 78 | 79 | try { 80 | new ConfigBuild(project).createSubstrateDispatcher(); 81 | } catch (Exception e) { 82 | throw new GradleException("Error creating Substrate Dispatcher: " + e); 83 | } 84 | 85 | try { 86 | // folder 87 | Path path = Path.of(project.getProjectDir().getAbsolutePath(), "src", "main", "resources", "META-INF", "native-image"); 88 | if (Files.exists(path)) { 89 | // TODO: Delete files 90 | // otherwise it keeps merging results from different runs 91 | // and config files might get outdated. 92 | } else { 93 | Files.createDirectories(path); 94 | } 95 | 96 | // Create filter file to exclude platform classes 97 | try { 98 | createFilterFile(path.resolve("filter-file.json").toString()); 99 | } catch (IOException e) { 100 | throw new GradleException("Error generating agent filter", e); 101 | } 102 | 103 | JavaExec execTask = (JavaExec) project.getTasks().findByName(ApplicationPlugin.TASK_RUN_NAME); 104 | if (execTask == null) { 105 | throw new GradleException("Run task not found."); 106 | } 107 | 108 | // set java_home 109 | String binary = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows") ? "java.exe" : "java"; 110 | execTask.executable(Path.of(graalVMHome.toString(), "bin", binary).toString()); 111 | 112 | // set jvmargs 113 | var jvmArgs = List.of(AGENTLIB_NATIVE_IMAGE_AGENT_STRING); 114 | execTask.getJvmArgumentProviders().add(() -> jvmArgs); 115 | 116 | // run 117 | execTask.exec(); 118 | } catch (Exception e) { 119 | throw new GradleException("RunAgent failure: " + e); 120 | } 121 | } 122 | 123 | private Path getGraalHome() { 124 | String graalvmHome = clientExtension.getGraalvmHome(); 125 | if (graalvmHome == null) { 126 | graalvmHome = System.getenv("GRAALVM_HOME"); 127 | } 128 | if (graalvmHome == null) { 129 | throw new GradleException("GraalVM installation directory not found." + 130 | " Either set GRAALVM_HOME as an environment variable or" + 131 | " set graalvmHome in the client-plugin configuration"); 132 | } 133 | return Path.of(graalvmHome); 134 | } 135 | 136 | private void createFilterFile(String agentFilter) throws IOException { 137 | File agentDirFilter = new File(agentFilter); 138 | if (agentDirFilter.exists()) { 139 | agentDirFilter.delete(); 140 | } 141 | try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(agentDirFilter)))) { 142 | bw.write("{ \"rules\": [\n"); 143 | boolean ruleHasBeenWritten = false; 144 | for (String rule : AGENTLIB_EXCLUSION_RULES) { 145 | if (ruleHasBeenWritten) { 146 | bw.write(",\n"); 147 | } else { 148 | ruleHasBeenWritten = true; 149 | } 150 | bw.write(" {\"excludeClasses\" : \"" + rule + "\"}"); 151 | } 152 | bw.write("\n ]\n"); 153 | bw.write("}\n"); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/gluonhq/gradle/tasks/NativeRunTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, 2021, Gluon 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of the copyright holder nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package com.gluonhq.gradle.tasks; 31 | 32 | import javax.inject.Inject; 33 | 34 | import org.gradle.api.Project; 35 | import org.gradle.api.tasks.TaskAction; 36 | 37 | import com.gluonhq.substrate.SubstrateDispatcher; 38 | 39 | public class NativeRunTask extends NativeBaseTask { 40 | @Inject 41 | public NativeRunTask(Project project) { 42 | super(project); 43 | } 44 | 45 | @TaskAction 46 | public void action() { 47 | getProject().getLogger().info("ClientNativeRun action"); 48 | 49 | try { 50 | SubstrateDispatcher dispatcher = new ConfigBuild(project).createSubstrateDispatcher(); 51 | dispatcher.nativeRun(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | --------------------------------------------------------------------------------