diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..996a6bf --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,18 @@ +version: 2.1 + +executors: + linux: + docker: + - image: cimg/openjdk:21.0 + +jobs: + unit-test: + executor: linux + steps: + - checkout + - run: ./gradlew test + +workflows: + build: + jobs: + - unit-test diff --git a/.editorconfig b/.editorconfig index ba53850..926de6b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,5 +14,14 @@ max_line_length = 80 [*.{kt,kts}] indent_size = 4 max_line_length = 100 -ktlint_standard_trailing-comma-on-call-site = disabled -ktlint_standard_trailing-comma-on-declaration-site = disabled +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_name_count_to_use_star_import = 2147483647 +ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 +ij_kotlin_packages_to_use_import_on_demand = unset +ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset +ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset +ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = unset + +[*.gradle] +indent_size = 4 +max_line_length = 100 diff --git a/.gitignore b/.gitignore index 65f9b19..1bd3e48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # macOS -**/.DS_Store +.DS_Store +._* # Gradle .gradle/ @@ -10,3 +11,6 @@ local.properties *.iml .idea/ out/ + +# VSCode +bin/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 91b5530..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -os: linux -language: java -jdk: openjdk8 - -script: - - ./gradlew check --info - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ diff --git a/README.md b/README.md index 1d8ad55..2a7e0db 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Travis CI](https://img.shields.io/travis/com/hendraanggrian/packaging-gradle-plugin)](https://travis-ci.com/github/hendraanggrian/packaging-gradle-plugin/) -[![Plugin Portal](https://img.shields.io/maven-metadata/v.svg?label=plugin-portal&metadataUrl=https%3A%2F%2Fplugins.gradle.org%2Fm2%2Fcom%2Fhendraanggrian%2Fpackaging%2Fcom.hendraanggrian.packaging.gradle.plugin%2Fmaven-metadata.xml)](https://plugins.gradle.org/plugin/com.hendraanggrian.packaging) -[![OpenJDK](https://img.shields.io/badge/jdk-17%2B-informational)](https://openjdk.java.net/projects/jdk/17/) +[![CircleCI](https://img.shields.io/circleci/build/gh/hanggrian/packaging-gradle-plugin)](https://app.circleci.com/pipelines/github/hanggrian/packaging-gradle-plugin/) +[![Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/com.hanggrian.packaging)](https://plugins.gradle.org/plugin/com.hanggrian.packaging) +[![Java](https://img.shields.io/badge/java-17+-informational)](https://docs.oracle.com/javase/17/) # Packaging Gradle Plugin @@ -9,18 +9,13 @@ Gradle plugin that wraps JARs into native bundle for Windows, macOS, and Linux. - Complete customization for each distribution. - Pack multiple distributions with a single task. -| Version | Method | -| --- | --- | -| 0.1 | [packr](https://github.com/libgdx/packr/) | -| 0.2+ | [jpackage](https://docs.oracle.com/en/java/javase/14/jpackage/packaging-overview.html) | - ## Download Using plugins DSL: ```gradle plugins { - id('com.hendraanggrian.packaging') version "$version" + id('com.hanggrian.packaging') version "$version" } ``` @@ -32,11 +27,11 @@ buildscript { gradlePluginPortal() } dependencies { - classpath("com.hendraanggrian:packaging-gradle-plugin:$version") + classpath("com.hanggrian:packaging-gradle-plugin:$version") } } -apply plugin: 'com.hendraanggrian.packaging' +apply plugin: 'com.hanggrian.packaging' ``` ## Usage @@ -47,30 +42,11 @@ configuration. ```gradle packaging { - executable.set('example') - classpath.set(new File('path/to/jar')) + appName.set('Custom Directory') mainClass.set('com.example.App') - vmArgs.addAll('-Xmx1G') - resources.addAll(new File('image.jpg'), new File('path/to/other.jpg')) - minimizeJre.set('hard') - outputDirectory.set(new File('my/folder')) + modules = ['javafx.controls', 'javafx.graphics'] + modulePaths.add(new File('/path/to/javafx-sdk/lib')) verbose.set(true) - autoOpen.set(true) -} - -tasks { - packWindows64 { - executable.set('example64') - vmArgs.add('-Xdebug') - appName.set('Example Windows 64-bit') - jdk.set('path/to/windows_64_jdk') - } - packMacOS { - name.set('Example.app') - jdk.set('path/to/mac_jdk') - icon.set(new File('path/to/mac_icon.icns')) - bundleId.set('com.example.app') - } } ``` @@ -81,7 +57,7 @@ with `installDist` command. ```gradle apply plugin: 'application' -apply plugin: 'com.hendraanggrian.packaging' +apply plugin: 'com.hanggrian.packaging' application { applicationName = 'My App' diff --git a/build.gradle.kts b/build.gradle.kts index 3763a40..6516879 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,7 @@ +val releaseGroup: String by project +val releaseVersion: String by project + allprojects { - group = RELEASE_GROUP - version = RELEASE_VERSION + group = releaseGroup + version = releaseVersion } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index a234eec..0000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -repositories.mavenCentral() - -plugins { - `kotlin-dsl` -} - -sourceSets.main { - java.srcDir("src") -} diff --git a/buildSrc/src/KtLint.kt b/buildSrc/src/KtLint.kt deleted file mode 100644 index 97ca27f..0000000 --- a/buildSrc/src/KtLint.kt +++ /dev/null @@ -1,52 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ModuleDependency -import org.gradle.api.attributes.Bundling -import org.gradle.api.tasks.JavaExec -import org.gradle.kotlin.dsl.getValue -import org.gradle.kotlin.dsl.invoke -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.provideDelegate -import org.gradle.kotlin.dsl.register -import org.gradle.kotlin.dsl.registering -import org.gradle.language.base.plugins.LifecycleBasePlugin - -val Project.ktlint: Configuration - get() { - var ktlint = configurations.findByName("ktlint") - if (ktlint == null) { - ktlint = configurations.create("ktlint") - val outputDir = "${project.buildDir}/reports/ktlint/" - val inputFiles = project.fileTree(mapOf("dir" to "src", "include" to "**/*.kt")) - tasks { - val ktlintCheck by registering(JavaExec::class) { - group = LifecycleBasePlugin.VERIFICATION_GROUP - inputs.files(inputFiles) - outputs.dir(outputDir) - description = "Check Kotlin code style." - classpath(ktlint) - mainClass.set("com.pinterest.ktlint.Main") - args = listOf("src/**/*.kt") - } - named("check") { - dependsOn(ktlintCheck) - } - register("ktlintFormat") { - group = "formatting" - inputs.files(inputFiles) - outputs.dir(outputDir) - description = "Fix Kotlin code style deviations." - classpath(ktlint) - mainClass.set("com.pinterest.ktlint.Main") - args = listOf("-F", "src/**/*.kt") - } - } - } - return ktlint - } - -fun Project.configureKtlint(dependency: ModuleDependency) { - dependency.attributes { - attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) - } -} diff --git a/buildSrc/src/Releases.kt b/buildSrc/src/Releases.kt deleted file mode 100644 index 9d41a77..0000000 --- a/buildSrc/src/Releases.kt +++ /dev/null @@ -1,9 +0,0 @@ -const val DEVELOPER_ID = "hendraanggrian" -const val DEVELOPER_NAME = "Hendra Anggrian" -const val DEVELOPER_URL = "https://github.com/$DEVELOPER_ID/" - -const val RELEASE_GROUP = "com.hendraanggrian.packaging" -const val RELEASE_ARTIFACT = "packaging-gradle-plugin" -const val RELEASE_VERSION = "0.2" -const val RELEASE_DESCRIPTION = "Start making native distributions for your JAR" -const val RELEASE_URL = "https://github.com/$DEVELOPER_ID/$RELEASE_ARTIFACT/" diff --git a/gradle.properties b/gradle.properties index 36caeb5..b9bdbd3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,13 @@ -# Gradle +developerId=hanggrian +developerName=Hendra Anggrian +developerUrl=https://github.com/hanggrian/ +releaseGroup=com.hanggrian.packaging +releaseArtifact=packaging-gradle-plugin +releaseVersion=0.1 +releaseDescription=Start making native distributions for your JAR +releaseUrl=https://github.com/hanggrian/packaging-gradle-plugin/ + org.gradle.parallel=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 -# IDEA kotlin.code.style=official diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 001b738..5aeb192 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,20 @@ [versions] -jdk = "17" -kotlin = "1.8.10" -ktor = "1.6.7" +jdk = "21" +jre = "17" +kotlin = "1.9.20" +ktlint = "1.3.1" [plugins] dokka = { id = "org.jetbrains.dokka", version.ref = "kotlin" } -kotlinx-kover = "org.jetbrains.kotlinx.kover:0.6.1" -gradle-publish = "com.gradle.plugin-publish:1.1.0" -git-publish = "org.ajoberstar.git-publish:4.1.1" -pages = "com.hendraanggrian.pages:0.1" +ktlint = "org.jlleitschuh.gradle.ktlint:12.1.1" +gradle-publish = "com.gradle.plugin-publish:1.2.1" +git-publish = "org.ajoberstar.git-publish:4.2.2" +pages = "com.hanggrian.pages:0.2" [libraries] # lint -ktlint = "com.pinterest:ktlint:0.48.2" -rulebook-ktlint = "com.hendraanggrian.rulebook:rulebook-ktlint:0.2" +rulebook-ktlint = "com.hanggrian.rulebook:rulebook-ktlint:0.1" # main -kotlinx-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" osdetector = "com.google.gradle:osdetector-gradle-plugin:1.7.2" # test -truth = "com.google.truth:truth:1.1.3" +truth = "com.google.truth:truth:1.4.4" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a7..2c35211 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c85a1f..09523c0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cb..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,7 +85,9 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -130,10 +134,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -141,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -149,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,11 +205,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 6689b85..9b42019 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/packaging-gradle-plugin/build.gradle.kts b/packaging-gradle-plugin/build.gradle.kts index 7ec00a7..a827f76 100644 --- a/packaging-gradle-plugin/build.gradle.kts +++ b/packaging-gradle-plugin/build.gradle.kts @@ -1,34 +1,64 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +val developerId: String by project +val releaseArtifact: String by project +val releaseGroup: String by project +val releaseDescription: String by project +val releaseUrl: String by project + +val jdkVersion = JavaLanguageVersion.of(libs.versions.jdk.get()) +val jreVersion = JavaLanguageVersion.of(libs.versions.jre.get()) + plugins { kotlin("jvm") version libs.versions.kotlin alias(libs.plugins.dokka) + alias(libs.plugins.ktlint) alias(libs.plugins.gradle.publish) } -kotlin.jvmToolchain(libs.versions.jdk.get().toInt()) +kotlin { + jvmToolchain(jdkVersion.asInt()) + explicitApi() +} + +ktlint.version.set(libs.versions.ktlint.get()) gradlePlugin { - website.set(RELEASE_URL) - vcsUrl.set("$RELEASE_URL.git") + website.set(releaseUrl) + vcsUrl.set("https://github.com/$developerId/$releaseArtifact.git") plugins.register("packagingPlugin") { - id = RELEASE_GROUP + id = releaseGroup + implementationClass = "$releaseGroup.PackagingPlugin" displayName = "Packaging Plugin" - description = RELEASE_DESCRIPTION + description = releaseDescription tags.set(listOf("packaging", "jpackage", "native", "installer", "bundle")) - implementationClass = "$RELEASE_GROUP.PackagingPlugin" } testSourceSets(sourceSets.test.get()) } dependencies { - ktlint(libs.ktlint, ::configureKtlint) - ktlint(libs.rulebook.ktlint) + ktlintRuleset(libs.rulebook.ktlint) + compileOnly(kotlin("gradle-plugin-api")) + implementation(gradleKotlinDsl()) implementation(libs.osdetector) + testImplementation(gradleTestKit()) testImplementation(kotlin("test-junit", libs.versions.kotlin.get())) + testImplementation(libs.truth) } -tasks.dokkaHtml { - outputDirectory.set(buildDir.resolve("dokka/dokka/")) +tasks { + compileJava { + options.release = jreVersion.asInt() + } + compileKotlin { + compilerOptions.jvmTarget + .set(JvmTarget.fromTarget(JavaVersion.toVersion(jreVersion).toString())) + } + + dokkaHtml { + outputDirectory.set(layout.buildDirectory.dir("dokka/dokka/")) + } } diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/Internals.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/Internals.kt new file mode 100644 index 0000000..550afeb --- /dev/null +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/Internals.kt @@ -0,0 +1,169 @@ +package com.hanggrian.packaging + +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.setProperty +import java.io.File + +/** Reverse the effect of [kr.motd.maven.os.Detector.normalizeArch]. */ +internal fun denormalizeArch(value: String): String = + when { + value.startsWith("x86_64") -> "x64" + value.startsWith("x86_32") -> "x32" + value.startsWith("itanium_64") -> "itanium64" + value.startsWith("itanium_32") -> "itanium32" + value.startsWith("sparc_32") -> "sparc32" + value.startsWith("sparc_64") -> "sparc64" + value.startsWith("arm_32") -> "arm32" + value.startsWith("aarch_64") -> "aarch64" + value.startsWith("mips_32") -> "mips32" + value.startsWith("mipsel_32") -> "mips32el" + value.startsWith("mips_64") -> "mips64" + value.startsWith("mipsel_64") -> "mips64el" + value.startsWith("ppc_32") -> "ppc32" + value.startsWith("ppcle_32") -> "ppc32le" + value.startsWith("ppc_64") -> "ppc64" + value.startsWith("ppcle_64") -> "ppc64le" + value.startsWith("s390_32") -> "s390" + value.startsWith("s390_64") -> "s390x" + else -> value + } + +internal abstract class PlatformOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpec) : + PackSpec { + //region Generic Options + override val appVersion: Property = + objects + .property() + .convention(defaultPackSpec.appVersion) + + override val copyright: Property = + objects + .property() + .convention(defaultPackSpec.copyright) + + override val appDescription: Property = + objects + .property() + .convention(defaultPackSpec.appDescription) + + override val appName: Property = + objects + .property() + .convention(defaultPackSpec.appName) + + override val outputDirectory: DirectoryProperty = + objects + .directoryProperty() + .convention(defaultPackSpec.outputDirectory) + + override val vendor: Property = + objects + .property() + .convention(defaultPackSpec.vendor) + + override val verbose: Property = + objects + .property() + .convention(defaultPackSpec.verbose) + //endregion + + //region Options for creating the runtime image + override val modules: SetProperty = + objects + .setProperty() + .convention(defaultPackSpec.modules) + + override val modulePaths: SetProperty = + objects + .setProperty() + .convention(defaultPackSpec.modulePaths) + + override val bindServices: Property = + objects + .property() + .convention(defaultPackSpec.bindServices) + + override val runtimeImage: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.runtimeImage) + //endregion + + //region Options for creating the application image + override val icon: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.icon) + + override val inputDirectory: DirectoryProperty = + objects + .directoryProperty() + .convention(defaultPackSpec.inputDirectory) + //endregion + + //region Options for creating the application launcher(s) + override val launcher: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.launcher) + + override val args: ListProperty = + objects + .listProperty() + .convention(defaultPackSpec.args) + + override val javaArgs: ListProperty = + objects + .listProperty() + .convention(defaultPackSpec.javaArgs) + + override val mainClass: Property = + objects + .property() + .convention(defaultPackSpec.mainClass) + + override val mainJar: Property = + objects + .property() + .convention(defaultPackSpec.mainJar) + + override val mainModule: Property = + objects + .property() + .convention(defaultPackSpec.mainModule) + //endregion + + //region Options for creating the application package + override val appImage: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.appImage) + + override val fileAssociations: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.fileAssociations) + + override val installDirectory: DirectoryProperty = + objects + .directoryProperty() + .convention(defaultPackSpec.installDirectory) + + override val license: RegularFileProperty = + objects + .fileProperty() + .convention(defaultPackSpec.license) + + override val resourcesDirectory: DirectoryProperty = + objects + .directoryProperty() + .convention(defaultPackSpec.resourcesDirectory) + //endregion +} diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/LinuxOptions.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/LinuxOptions.kt similarity index 73% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/LinuxOptions.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/LinuxOptions.kt index a83779f..61cc438 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/LinuxOptions.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/LinuxOptions.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.api.model.ObjectFactory @@ -6,37 +6,38 @@ import org.gradle.api.model.ObjectFactory * Platform-specific options than can be configured using [PackagingExtension.linux]. * This [PackSpec] will also inherit configuration from extension. */ -interface LinuxOptions : PackSpec { +public interface LinuxOptions : PackSpec { //region Platform dependent options for creating the application package + /** Name for Linux package, defaults to the application name. */ - var packageName: String? + public var packageName: String? /** Maintainer for .deb bundle. */ - var debMaintainer: String? + public var debMaintainer: String? /** Menu group this application is placed in. */ - var menuGroup: String? + public var menuGroup: String? /** Required packages or capabilities for the application. */ - var packageDependencies: String? + public var packageDependencies: String? /** Type of the license ("License: " of the RPM .spec). */ - var rpmLicenseType: String? + public var rpmLicenseType: String? /** Release value of the RPM .spec file or Debian revision value of the DEB control file. */ - var appRelease: String? + public var appRelease: String? /** Group value of the RPM .spec file or Section value of DEB control file. */ - var appCategory: String? + public var appCategory: String? /** Creates a shortcut for the application. */ - var shortcut: Boolean + public var isShortcut: Boolean //endregion } internal class LinuxOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpec) : - PlatformOptionsImpl(objects, defaultPackSpec), LinuxOptions { - + PlatformOptionsImpl(objects, defaultPackSpec), + LinuxOptions { //region Platform dependent options for creating the application package override var packageName: String? = null override var debMaintainer: String? = null @@ -45,6 +46,6 @@ internal class LinuxOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpe override var rpmLicenseType: String? = null override var appRelease: String? = null override var appCategory: String? = null - override var shortcut: Boolean = false + override var isShortcut: Boolean = false //endregion } diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/MacOptions.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/MacOptions.kt similarity index 75% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/MacOptions.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/MacOptions.kt index e928d5b..0bc37e0 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/MacOptions.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/MacOptions.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.api.model.ObjectFactory import java.io.File @@ -7,39 +7,40 @@ import java.io.File * Platform-specific options than can be configured using [PackagingExtension.mac]. * This [PackSpec] will also inherit configuration from extension. */ -interface MacOptions : PackSpec { +public interface MacOptions : PackSpec { //region Platform dependent option for creating the application launcher + /** An identifier that uniquely identifies the application for macOSX. */ - var packageIdentifier: String? + public var packageIdentifier: String? /** Name of the application as it appears in the Menu Bar. */ - var packageName: String? + public var packageName: String? /** * When signing the application bundle, this value is prefixed to all components that need to be * signed that don't have an existing bundle identifier. */ - var bundleSigningPrefix: String? + public var bundleSigningPrefix: String? /** Request that the bundle be signed. */ - var sign: Boolean + public var isSign: Boolean /** Path of the keychain to search for the signing identity. */ - var signingKeychain: File? + public var signingKeychain: File? /** Team name portion in Apple signing identities' names. */ - var signingKeyUserName: String? + public var signingKeyUserName: String? //endregion } internal class MacOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpec) : - PlatformOptionsImpl(objects, defaultPackSpec), MacOptions { - + PlatformOptionsImpl(objects, defaultPackSpec), + MacOptions { //region Platform dependent option for creating the application launcher override var packageIdentifier: String? = null override var packageName: String? = null override var bundleSigningPrefix: String? = null - override var sign: Boolean = false + override var isSign: Boolean = false override var signingKeychain: File? = null override var signingKeyUserName: String? = null //endregion diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackConfigurationDsl.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackConfigurationDsl.kt similarity index 73% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackConfigurationDsl.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackConfigurationDsl.kt index c817511..704634a 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackConfigurationDsl.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackConfigurationDsl.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging /** * Forces platform packaging configurations to be on the same level, such as: @@ -13,4 +13,4 @@ package com.hendraanggrian.packaging */ @DslMarker @Target(AnnotationTarget.CLASS) -annotation class PackConfigurationDsl +public annotation class PackConfigurationDsl diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackSpec.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackSpec.kt similarity index 66% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackSpec.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackSpec.kt index db13159..f4e5ce0 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackSpec.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackSpec.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -12,119 +12,112 @@ import java.io.File * specific platform. */ @PackConfigurationDsl -interface PackSpec { +public interface PackSpec { //region Generic Options + /** Version of the application and/or package`. Default is project's version. */ - val appVersion: Property + public val appVersion: Property /** Copyright for the application. */ - val copyright: Property + public val copyright: Property /** Description of the application. */ - val appDescription: Property + public val appDescription: Property /** Name of the application and/or package. Default is project's name. */ - val appName: Property + public val appName: Property /** * Path where generated output file is placed. Default is `build/install` in project directory. */ - val outputDirectory: DirectoryProperty - - /** Returns [outputDirectory] represented as [File]. */ - val outputDir: File get() = outputDirectory.asFile.get() + public val outputDirectory: DirectoryProperty /** Vendor of the application. */ - val vendor: Property + public val vendor: Property /** Enables verbose output. Default is false. */ - val verbose: Property + public val verbose: Property //endregion //region Options for creating the runtime image + /** * This module list, along with the main module (if specified) will be passed to jlink as * the `--add-module` argument. */ - val modules: SetProperty + public val modules: SetProperty /** Each path is either a directory of modules or the path to a modular jar. */ - val modulePaths: SetProperty + public val modulePaths: SetProperty /** * Pass on --bind-services option to jlink (which will link in service provider modules and * their dependencies). */ - val bindServices: Property + public val bindServices: Property /** Path of the predefined runtime image that will be copied into the application image. */ - val runtimeImage: RegularFileProperty + public val runtimeImage: RegularFileProperty //endregion //region Options for creating the application image + /** Path of the icon of the application bundle. */ - val icon: RegularFileProperty + public val icon: RegularFileProperty /** Path of the input directory that contains the files to be packaged. */ - val inputDirectory: DirectoryProperty - - /** Returns [inputDirectory] represented as [File]. */ - val inputDir: File get() = inputDirectory.asFile.get() + public val inputDirectory: DirectoryProperty //endregion //region Options for creating the application launcher(s) + /** * Name of launcher, and a path to a Properties file that contains a list of key, value pairs. */ - val launcher: RegularFileProperty + public val launcher: RegularFileProperty /** * Command line arguments to pass to the main class if no command line arguments are given to * the launcher. */ - val args: ListProperty + public val args: ListProperty /** Options to pass to the Java runtime. */ - val javaArgs: ListProperty + public val javaArgs: ListProperty /** Qualified name of the application main class to execute. */ - val mainClass: Property + public val mainClass: Property /** * The main JAR of the application; containing the main class (specified as a path relative to * the input path). Default is `$projectName-$projectVersion.jar` */ - val mainJar: Property + public val mainJar: Property /** * The main module (and optionally main class) of the application This module must be located on * the module path. */ - val mainModule: Property + public val mainModule: Property //endregion //region Options for creating the application package + /** * Location of the predefined application image that is used to build an installable package. */ - val appImage: RegularFileProperty + public val appImage: RegularFileProperty /** Path to a Properties file that contains list of key, value pairs. */ - val fileAssociations: RegularFileProperty + public val fileAssociations: RegularFileProperty /** Absolute path of the installation directory of the application on OS X or Linux. */ - val installDirectory: DirectoryProperty - - /** Returns [installDirectory] represented as [File]. */ - val installDir: File get() = installDirectory.asFile.get() + public val installDirectory: DirectoryProperty /** Path to the license file. */ - val license: RegularFileProperty + public val license: RegularFileProperty /** Path to override jpackage resources, *not application resources*. */ - val resourcesDirectory: DirectoryProperty - - /** Returns [resourcesDirectory] represented as [File]. */ - val resourcesDir: File get() = resourcesDirectory.asFile.get() + public val resourcesDirectory: DirectoryProperty //endregion } diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingExtension.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingExtension.kt similarity index 61% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingExtension.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingExtension.kt index 3f0fb88..93f19e2 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingExtension.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingExtension.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.api.Action @@ -7,22 +7,22 @@ import org.gradle.api.Action * this [PackSpec] will be passed into platform-specific specs. */ @PackConfigurationDsl -interface PackagingExtension : PackSpec { +public interface PackagingExtension : PackSpec { /** Windows options that will be packaged. */ - val windows: WindowsOptions - - /** Configures the windows options that will be packaged. */ - fun windows(action: Action) + public val windows: WindowsOptions /** Mac options that will be packaged. */ - val mac: MacOptions - - /** Configures the Mac options that will be packaged. */ - fun mac(action: Action) + public val mac: MacOptions /** Linux options that will be packaged. */ - val linux: LinuxOptions + public val linux: LinuxOptions + + /** Configures the windows options that will be packaged. */ + public fun windows(action: Action) + + /** Configures the Mac options that will be packaged. */ + public fun mac(action: Action) /** Configures the linux options that will be packaged. */ - fun linux(action: Action) + public fun linux(action: Action) } diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingPlugin.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingPlugin.kt new file mode 100644 index 0000000..caade15 --- /dev/null +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/PackagingPlugin.kt @@ -0,0 +1,343 @@ +package com.hanggrian.packaging + +import com.google.gradle.osdetector.OsDetector +import com.google.gradle.osdetector.OsDetectorPlugin +import com.hanggrian.packaging.internal.DefaultPackagingExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.distribution.plugins.DistributionPlugin +import org.gradle.api.plugins.ApplicationPlugin +import org.gradle.api.plugins.JavaApplication +import org.gradle.api.tasks.Exec +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.getByName +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.register +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform + +/** + * Plugin that creates native bundles for your JAR. + * + * @see packaging-gradle-plugin + */ +public class PackagingPlugin : Plugin { + override fun apply(project: Project) { + project.pluginManager.apply(OsDetectorPlugin::class) + val hasApplicationPlugin = + project.pluginManager.hasPlugin(ApplicationPlugin.APPLICATION_PLUGIN_NAME) + + val packaging = + project.extensions.create( + PackagingExtension::class, + "packaging", + DefaultPackagingExtension::class, + project.objects, + project.layout, + ) + val osDetector = project.extensions.getByType() + val os = DefaultNativePlatform.getCurrentOperatingSystem() + when { + os.isWindows -> { + createPackTask(project, TASK_DIST_WINDOWS_EXE, hasApplicationPlugin) + createPackTask(project, TASK_DIST_WINDOWS_MSI, hasApplicationPlugin) + } + os.isMacOsX -> { + createPackTask(project, TASK_DIST_MAC_DMG, hasApplicationPlugin) + createPackTask(project, TASK_DIST_MAC_PKG, hasApplicationPlugin) + } + os.isLinux -> { + createPackTask(project, TASK_DIST_LINUX_DEB, hasApplicationPlugin) + createPackTask(project, TASK_DIST_LINUX_RPM, hasApplicationPlugin) + } + } + + project.afterEvaluate { + packaging.appVersion.convention(project.version.toString()) + packaging.appName.convention(project.name) + packaging.mainJar.convention("${project.name}-${project.version}.jar") + if (hasApplicationPlugin) { + val application = + project.extensions + .getByName(ApplicationPlugin.APPLICATION_PLUGIN_NAME) + packaging.appName.convention(application.applicationName) + packaging.inputDirectory.convention( + project.layout.buildDirectory.dir("install/${application.applicationName}/lib"), + ) + packaging.mainClass.convention(application.mainClass) + } + + val commandLines = mutableListOf() + packaging as DefaultPackagingExtension + when { + os.isWindows -> { + commandLines.append(packaging.windowsOptions ?: packaging) + modifyPackTask( + project, + TASK_DIST_WINDOWS_EXE, + commandLines, + packaging, + osDetector, + ) + modifyPackTask( + project, + TASK_DIST_WINDOWS_MSI, + commandLines, + packaging, + osDetector, + ) + } + os.isMacOsX -> { + commandLines.append(packaging.macOptions ?: packaging) + modifyPackTask(project, TASK_DIST_MAC_DMG, commandLines, packaging, osDetector) + modifyPackTask(project, TASK_DIST_MAC_PKG, commandLines, packaging, osDetector) + } + os.isLinux -> { + commandLines.append(packaging.linuxOptions ?: packaging) + modifyPackTask( + project, + TASK_DIST_LINUX_DEB, + commandLines, + packaging, + osDetector, + ) + modifyPackTask( + project, + TASK_DIST_LINUX_RPM, + commandLines, + packaging, + osDetector, + ) + } + } + } + } + + private fun createPackTask(project: Project, taskName: String, hasApplicationPlugin: Boolean) { + project.tasks.register(taskName) { + if (hasApplicationPlugin) { + dependsOn(DistributionPlugin.TASK_INSTALL_NAME) + } + group = GROUP + description = "Bundles the project as a native package in platform-specific format." + } + } + + private fun modifyPackTask( + project: Project, + taskName: String, + commandLines: List, + packaging: PackagingExtension, + detector: OsDetector, + ) { + val jpackageType = taskName.takeLast(3).lowercase() + val outputFile = + packaging + .outputDirectory + .asFile + .get() + .resolve("${packaging.appName.get()}-${packaging.appVersion.get()}.$jpackageType") + project.tasks.getByName(taskName) { + commandLine(listOf("jpackage", "--type", jpackageType) + commandLines) + doLast { + outputFile.renameTo( + outputFile.parentFile.resolve( + packaging.appName.get().replace(" ", "") + + "-${packaging.appVersion.get()}" + + "-${denormalizeArch(detector.arch)}" + + ".$jpackageType", + ), + ) + } + } + } + + private fun MutableList.append(spec: PackSpec) { + add("--app-version") + add(spec.appVersion.get()) + + add("--name") + add(spec.appName.get()) + + add("--dest") + add(spec.outputDirectory.asFile.get().absolutePath) + + add("--input") + add(spec.inputDirectory.asFile.get().absolutePath) + + add("--main-class") + add(spec.mainClass.get()) + + add("--main-jar") + add(spec.mainJar.get()) + + spec.copyright.orNull?.let { + add("--copyright") + add(it) + } + spec.appDescription.orNull?.let { + add("--description") + add(it) + } + spec.vendor.orNull?.let { + add("--vendor") + add(it) + } + if (spec.verbose.get()) { + add("--verbose") + } + spec.modules.orNull?.forEach { + add("--add-modules") + add(it) + } + spec.modulePaths.orNull?.forEach { + add("--module-path") + add(it.absolutePath) + } + spec.bindServices.orNull?.let { + add("--bind-services") + add(it) + } + spec.runtimeImage.orNull?.let { + add("--runtime-image") + add(it.asFile.absolutePath) + } + spec.icon.orNull?.let { + add("--icon") + add(it.asFile.absolutePath) + } + spec.launcher.orNull?.let { + add("--add-launcher") + add(it.asFile.absolutePath) + } + spec.args.orNull?.forEach { + add("--arguments") + add("'$it'") + } + spec.javaArgs.orNull?.forEach { + add("--java-options") + add(it) + } + spec.mainModule.orNull?.let { + add("--module") + add(it) + } + spec.appImage.orNull?.let { + add("--app-image") + add(it.asFile.absolutePath) + } + spec.fileAssociations.orNull?.let { + add("--file-associations") + add(it.asFile.absolutePath) + } + spec.installDirectory.orNull?.let { + add("--install-dir") + add(it.asFile.absolutePath) + } + spec.license.orNull?.let { + add("--license-file") + add(it.asFile.absolutePath) + } + spec.resourcesDirectory.orNull?.let { + add("--resource-dir") + add(it.asFile.absolutePath) + } + + when (spec) { + is WindowsOptions -> { + if (spec.isConsole) { + add("--win-console") + } + if (spec.isDirectoryChooser) { + add("--win-dir-chooser") + } + if (spec.isMenu) { + add("--win-menu") + } + spec.menuGroup?.let { + add("--win-menu-group") + add(it) + } + if (spec.isPerUserInstall) { + add("--win-per-user-install") + } + if (spec.isShortcut) { + add("--win-shortcut") + } + spec.upgradeUuid?.let { + add("--win-upgrade-uuid") + add(it) + } + } + is MacOptions -> { + spec.packageIdentifier?.let { + add("--mac-package-identifier") + add(it) + } + spec.packageName?.let { + add("--mac-package-name") + add(it) + } + spec.bundleSigningPrefix?.let { + add("--mac-bundle-signing-prefix") + add(it) + } + if (spec.isSign) { + add("--mac-sign") + } + spec.signingKeychain?.let { + add("--mac-signing-keychain") + add(it.absolutePath) + } + spec.signingKeyUserName?.let { + add("--mac-signing-key-user-name") + add(it) + } + } + is LinuxOptions -> { + spec.packageName?.let { + add("--linux-package-name") + add(it) + } + spec.debMaintainer?.let { + add("--linux-deb-maintainer") + add(it) + } + spec.menuGroup?.let { + add("--linux-menu-group") + add(it) + } + spec.packageDependencies?.let { + add("--linux-package-deps") + add(it) + } + spec.rpmLicenseType?.let { + add("--linux-rpm-license-type") + add(it) + } + spec.appRelease?.let { + add("--linux-app-release") + add(it) + } + spec.appCategory?.let { + add("--linux-app-category") + add(it) + } + if (spec.isShortcut) { + add("--linux-shortcut") + } + } + } + } + + public companion object { + // the same as DistributionPlugin.DISTRIBUTION_GROUP + public const val GROUP: String = "distribution" + public const val TASK_DIST_WINDOWS_EXE: String = "distWindowsExe" + public const val TASK_DIST_WINDOWS_MSI: String = "distWindowsMsi" + public const val TASK_DIST_MAC_DMG: String = "distMacDmg" + public const val TASK_DIST_MAC_PKG: String = "distMacPkg" + public const val TASK_DIST_LINUX_DEB: String = "distLinuxDeb" + public const val TASK_DIST_LINUX_RPM: String = "distLinuxRpm" + } +} diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/WindowsOptions.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/WindowsOptions.kt similarity index 67% rename from packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/WindowsOptions.kt rename to packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/WindowsOptions.kt index 086f609..9ccf35d 100644 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/WindowsOptions.kt +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/WindowsOptions.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.api.model.ObjectFactory @@ -6,49 +6,51 @@ import org.gradle.api.model.ObjectFactory * Platform-specific options than can be configured using [PackagingExtension.windows]. * This [PackSpec] will also inherit configuration from extension. */ -interface WindowsOptions : PackSpec { +public interface WindowsOptions : PackSpec { //region Platform dependent option for creating the application launcher + /** * Creates a console launcher for the application, should be specified for application which * requires console interactions. */ - var console: Boolean + public var isConsole: Boolean //endregion //region Platform dependent options for creating the application package + /** Adds a dialog to enable the user to choose a directory in which the product is installed. */ - var directoryChooser: Boolean + public var isDirectoryChooser: Boolean /** Adds the application to the system menu. */ - var menu: Boolean + public var isMenu: Boolean /** Start Menu group this application is placed in. */ - var menuGroup: String? + public var menuGroup: String? /** Request to perform an install on a per-user basis. */ - var perUserInstall: Boolean + public var isPerUserInstall: Boolean /** Creates a desktop shortcut for the application. */ - var shortcut: Boolean + public var isShortcut: Boolean /** UUID associated with upgrades for this package. */ - var upgradeUuid: String? + public var upgradeUuid: String? //endregion } internal class WindowsOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpec) : - PlatformOptionsImpl(objects, defaultPackSpec), WindowsOptions { - + PlatformOptionsImpl(objects, defaultPackSpec), + WindowsOptions { //region Platform dependent option for creating the application launcher - override var console: Boolean = false + override var isConsole: Boolean = false //endregion //region Platform dependent options for creating the application package - override var directoryChooser: Boolean = false - override var menu: Boolean = false + override var isDirectoryChooser: Boolean = false + override var isMenu: Boolean = false override var menuGroup: String? = null - override var perUserInstall: Boolean = false - override var shortcut: Boolean = false + override var isPerUserInstall: Boolean = false + override var isShortcut: Boolean = false override var upgradeUuid: String? = null //endregion } diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/internal/DefaultPackagingExtension.kt b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/internal/DefaultPackagingExtension.kt new file mode 100644 index 0000000..362c327 --- /dev/null +++ b/packaging-gradle-plugin/src/main/kotlin/com/hanggrian/packaging/internal/DefaultPackagingExtension.kt @@ -0,0 +1,146 @@ +package com.hanggrian.packaging.internal + +import com.hanggrian.packaging.LinuxOptions +import com.hanggrian.packaging.LinuxOptionsImpl +import com.hanggrian.packaging.MacOptions +import com.hanggrian.packaging.MacOptionsImpl +import com.hanggrian.packaging.PackagingExtension +import com.hanggrian.packaging.WindowsOptions +import com.hanggrian.packaging.WindowsOptionsImpl +import org.gradle.api.Action +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.setProperty +import java.io.File + +public open class DefaultPackagingExtension( + private val objects: ObjectFactory, + layout: ProjectLayout, +) : PackagingExtension { + public var windowsOptions: WindowsOptions? = null + public var macOptions: MacOptions? = null + public var linuxOptions: LinuxOptions? = null + + //region Generic Options + final override val appVersion: Property = + objects.property() + + final override val copyright: Property = + objects.property() + + final override val appDescription: Property = + objects.property() + + final override val appName: Property = + objects.property() + + final override val outputDirectory: DirectoryProperty = + objects + .directoryProperty() + .convention(layout.buildDirectory.dir("install")) + + final override val vendor: Property = + objects.property() + + final override val verbose: Property = + objects + .property() + .convention(false) + //endregion + + //region Options for creating the runtime image + final override val modules: SetProperty = + objects.setProperty() + + final override val modulePaths: SetProperty = + objects.setProperty() + + final override val bindServices: Property = + objects.property() + + final override val runtimeImage: RegularFileProperty = + objects.fileProperty() + //endregion + + //region Options for creating the application image + final override val icon: RegularFileProperty = + objects.fileProperty() + + final override val inputDirectory: DirectoryProperty = + objects.directoryProperty() + //endregion + + //region Options for creating the application launcher(s) + final override val launcher: RegularFileProperty = + objects.fileProperty() + + final override val args: ListProperty = + objects.listProperty() + + final override val javaArgs: ListProperty = + objects.listProperty() + + final override val mainClass: Property = + objects.property() + + final override val mainJar: Property = + objects.property() + + final override val mainModule: Property = + objects.property() + //endregion + + //region Options for creating the application package + final override val appImage: RegularFileProperty = + objects.fileProperty() + + final override val fileAssociations: RegularFileProperty = + objects.fileProperty() + + final override val installDirectory: DirectoryProperty = + objects.directoryProperty() + + final override val license: RegularFileProperty = + objects.fileProperty() + + final override val resourcesDirectory: DirectoryProperty = + objects.directoryProperty() + //endregion + + final override val windows: WindowsOptions + get() { + if (windowsOptions == null) { + windowsOptions = WindowsOptionsImpl(objects, this) + } + return windowsOptions!! + } + + final override val mac: MacOptions + get() { + if (macOptions == null) { + macOptions = MacOptionsImpl(objects, this) + } + return macOptions!! + } + + final override val linux: LinuxOptions + get() { + if (linuxOptions == null) { + linuxOptions = LinuxOptionsImpl(objects, this) + } + return linuxOptions!! + } + + final override fun windows(action: Action): Unit = action.execute(windows) + + final override fun mac(action: Action): Unit = action.execute(mac) + + final override fun linux(action: Action): Unit = action.execute(linux) +} diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/Internals.kt b/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/Internals.kt deleted file mode 100644 index df5993e..0000000 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/Internals.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.hendraanggrian.packaging - -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.property -import org.gradle.kotlin.dsl.setProperty -import java.io.File - -/** Reverse the effect of [kr.motd.maven.os.Detector.normalizeArch]. */ -internal fun denormalizeArch(value: String): String = when { - value.startsWith("x86_64") -> "x64" - value.startsWith("x86_32") -> "x32" - value.startsWith("itanium_64") -> "itanium64" - value.startsWith("itanium_32") -> "itanium32" - value.startsWith("sparc_32") -> "sparc32" - value.startsWith("sparc_64") -> "sparc64" - value.startsWith("arm_32") -> "arm32" - value.startsWith("aarch_64") -> "arm64" - value.startsWith("mips_32") -> "mips32" - value.startsWith("mipsel_32") -> "mips32el" - value.startsWith("mips_64") -> "mips64" - value.startsWith("mipsel_64") -> "mips64el" - value.startsWith("ppc_32") -> "ppc32" - value.startsWith("ppcle_32") -> "ppc32le" - value.startsWith("ppc_64") -> "ppc64" - value.startsWith("ppcle_64") -> "ppc64le" - value.startsWith("s390_32") -> "s390" - value.startsWith("s390_64") -> "s390x" - else -> value -} - -internal abstract class PlatformOptionsImpl(objects: ObjectFactory, defaultPackSpec: PackSpec) : - PackSpec { - - //region Generic Options - override val appVersion: Property = objects.property() - .convention(defaultPackSpec.appVersion) - override val copyright: Property = objects.property() - .convention(defaultPackSpec.copyright) - override val appDescription: Property = objects.property() - .convention(defaultPackSpec.appDescription) - override val appName: Property = objects.property() - .convention(defaultPackSpec.appName) - override val outputDirectory: DirectoryProperty = objects.directoryProperty() - .convention(defaultPackSpec.outputDirectory) - override val vendor: Property = objects.property() - .convention(defaultPackSpec.vendor) - override val verbose: Property = objects.property() - .convention(defaultPackSpec.verbose) - //endregion - - //region Options for creating the runtime image - override val modules: SetProperty = objects.setProperty() - .convention(defaultPackSpec.modules) - override val modulePaths: SetProperty = objects.setProperty() - .convention(defaultPackSpec.modulePaths) - override val bindServices: Property = objects.property() - .convention(defaultPackSpec.bindServices) - override val runtimeImage: RegularFileProperty = objects.fileProperty() - .convention(defaultPackSpec.runtimeImage) - //endregion - - //region Options for creating the application image - override val icon: RegularFileProperty = objects.fileProperty().convention(defaultPackSpec.icon) - override val inputDirectory: DirectoryProperty = objects.directoryProperty() - .convention(defaultPackSpec.inputDirectory) - //endregion - - //region Options for creating the application launcher(s) - override val launcher: RegularFileProperty = objects.fileProperty() - .convention(defaultPackSpec.launcher) - override val args: ListProperty = objects.listProperty() - .convention(defaultPackSpec.args) - override val javaArgs: ListProperty = objects.listProperty() - .convention(defaultPackSpec.javaArgs) - override val mainClass: Property = objects.property() - .convention(defaultPackSpec.mainClass) - override val mainJar: Property = objects.property() - .convention(defaultPackSpec.mainJar) - override val mainModule: Property = objects.property() - .convention(defaultPackSpec.mainModule) - //endregion - - //region Options for creating the application package - override val appImage: RegularFileProperty = objects.fileProperty() - .convention(defaultPackSpec.appImage) - override val fileAssociations: RegularFileProperty = objects.fileProperty() - .convention(defaultPackSpec.fileAssociations) - override val installDirectory: DirectoryProperty = objects.directoryProperty() - .convention(defaultPackSpec.installDirectory) - override val license: RegularFileProperty = objects.fileProperty() - .convention(defaultPackSpec.license) - override val resourcesDirectory: DirectoryProperty = objects.directoryProperty() - .convention(defaultPackSpec.resourcesDirectory) - //endregion -} diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingPlugin.kt b/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingPlugin.kt deleted file mode 100644 index b165af9..0000000 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/PackagingPlugin.kt +++ /dev/null @@ -1,220 +0,0 @@ -package com.hendraanggrian.packaging - -import com.google.gradle.osdetector.OsDetector -import com.google.gradle.osdetector.OsDetectorPlugin -import com.hendraanggrian.packaging.internal.DefaultPackagingExtension -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.distribution.plugins.DistributionPlugin -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaApplication -import org.gradle.api.tasks.Exec -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.getByName -import org.gradle.kotlin.dsl.getByType -import org.gradle.kotlin.dsl.register -import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform - -/** - * Plugin that creates native bundles for your JAR. - * - * @see packaging-gradle-plugin - */ -class PackagingPlugin : Plugin { - companion object { - const val GROUP: String = - "distribution" // the same as DistributionPlugin.DISTRIBUTION_GROUP - const val TASK_DIST_WINDOWS_EXE: String = "distWindowsExe" - const val TASK_DIST_WINDOWS_MSI: String = "distWindowsMsi" - const val TASK_DIST_MAC_DMG: String = "distMacDmg" - const val TASK_DIST_MAC_PKG: String = "distMacPkg" - const val TASK_DIST_LINUX_DEB: String = "distLinuxDeb" - const val TASK_DIST_LINUX_RPM: String = "distLinuxRpm" - } - - override fun apply(project: Project) { - project.pluginManager.apply(OsDetectorPlugin::class) - val hasApplicationPlugin = - project.pluginManager.hasPlugin(ApplicationPlugin.APPLICATION_PLUGIN_NAME) - - val packaging = project.extensions.create( - PackagingExtension::class, - "packaging", - DefaultPackagingExtension::class, - project.objects, - project.layout - ) - val osDetector = project.extensions.getByType() - val os = DefaultNativePlatform.getCurrentOperatingSystem() - when { - os.isWindows -> { - createPackTask(project, TASK_DIST_WINDOWS_EXE, hasApplicationPlugin) - createPackTask(project, TASK_DIST_WINDOWS_MSI, hasApplicationPlugin) - } - os.isMacOsX -> { - createPackTask(project, TASK_DIST_MAC_DMG, hasApplicationPlugin) - createPackTask(project, TASK_DIST_MAC_PKG, hasApplicationPlugin) - } - os.isLinux -> { - createPackTask(project, TASK_DIST_LINUX_DEB, hasApplicationPlugin) - createPackTask(project, TASK_DIST_LINUX_RPM, hasApplicationPlugin) - } - } - - project.afterEvaluate { - packaging.appVersion.convention(project.version.toString()) - packaging.appName.convention(project.name) - packaging.mainJar.convention("${project.name}-${project.version}.jar") - if (hasApplicationPlugin) { - val application = project.extensions - .getByName(ApplicationPlugin.APPLICATION_PLUGIN_NAME) - packaging.appName.convention(application.applicationName) - packaging.inputDirectory.convention( - project.layout.buildDirectory.dir("install/${application.applicationName}/lib") - ) - packaging.mainClass.convention(application.mainClass) - } - - val commandLines = mutableListOf() - packaging as DefaultPackagingExtension - when { - os.isWindows -> { - commandLines.append(packaging.windowsOptions ?: packaging) - modifyPackTask( - project, - TASK_DIST_WINDOWS_EXE, - commandLines, - packaging, - osDetector - ) - modifyPackTask( - project, - TASK_DIST_WINDOWS_MSI, - commandLines, - packaging, - osDetector - ) - } - os.isMacOsX -> { - commandLines.append(packaging.macOptions ?: packaging) - modifyPackTask(project, TASK_DIST_MAC_DMG, commandLines, packaging, osDetector) - modifyPackTask(project, TASK_DIST_MAC_PKG, commandLines, packaging, osDetector) - } - os.isLinux -> { - commandLines.append(packaging.linuxOptions ?: packaging) - modifyPackTask( - project, - TASK_DIST_LINUX_DEB, - commandLines, - packaging, - osDetector - ) - modifyPackTask( - project, - TASK_DIST_LINUX_RPM, - commandLines, - packaging, - osDetector - ) - } - } - } - } - - private fun createPackTask(project: Project, taskName: String, hasApplicationPlugin: Boolean) { - project.tasks.register(taskName) { - if (hasApplicationPlugin) { - dependsOn(DistributionPlugin.TASK_INSTALL_NAME) - } - group = GROUP - description = "Bundles the project as a native package in platform-specific format." - } - } - - private fun modifyPackTask( - project: Project, - taskName: String, - commandLines: List, - packaging: PackagingExtension, - detector: OsDetector - ) { - val jpackageType = taskName.takeLast(3).lowercase() - val outputFile = packaging.outputDirectory.asFile.get() - .resolve("${packaging.appName.get()}-${packaging.appVersion.get()}.$jpackageType") - project.tasks.getByName(taskName) { - commandLine(listOf("jpackage", "--type", jpackageType) + commandLines) - doLast { - outputFile.renameTo( - outputFile.parentFile.resolve( - packaging.appName.get().replace(" ", "") + - "-${packaging.appVersion.get()}" + - "-${denormalizeArch(detector.arch)}" + - ".$jpackageType" - ) - ) - } - } - } - - private fun MutableList.append(spec: PackSpec) { - add("--app-version"); add(spec.appVersion.get()) - add("--name"); add(spec.appName.get()) - add("--dest"); add(spec.outputDirectory.asFile.get().absolutePath) - add("--input"); add(spec.inputDirectory.asFile.get().absolutePath) - add("--main-class"); add(spec.mainClass.get()) - add("--main-jar"); add(spec.mainJar.get()) - spec.copyright.orNull?.let { add("--copyright"); add(it) } - spec.appDescription.orNull?.let { add("--description"); add(it) } - spec.vendor.orNull?.let { add("--vendor"); add(it) } - if (spec.verbose.get()) { - add("--verbose") - } - spec.modules.orNull?.forEach { add("--add-modules"); add(it) } - spec.modulePaths.orNull?.forEach { add("--module-path"); add(it.absolutePath) } - spec.bindServices.orNull?.let { add("--bind-services"); add(it) } - spec.runtimeImage.orNull?.let { add("--runtime-image"); add(it.asFile.absolutePath) } - spec.icon.orNull?.let { add("--icon"); add(it.asFile.absolutePath) } - spec.launcher.orNull?.let { add("--add-launcher"); add(it.asFile.absolutePath) } - spec.args.orNull?.forEach { add("--arguments"); add("'$it'") } - spec.javaArgs.orNull?.forEach { add("--java-options"); add(it) } - spec.mainModule.orNull?.let { add("--module"); add(it) } - spec.appImage.orNull?.let { add("--app-image"); add(it.asFile.absolutePath) } - spec.fileAssociations.orNull?.let { - add("--file-associations"); add(it.asFile.absolutePath) - } - spec.installDirectory.orNull?.let { add("--install-dir"); add(it.asFile.absolutePath) } - spec.license.orNull?.let { add("--license-file"); add(it.asFile.absolutePath) } - spec.resourcesDirectory.orNull?.let { add("--resource-dir"); add(it.asFile.absolutePath) } - - when (spec) { - is WindowsOptions -> { - if (spec.console) add("--win-console") - if (spec.directoryChooser) add("--win-dir-chooser") - if (spec.menu) add("--win-menu") - spec.menuGroup?.let { add("--win-menu-group"); add(it) } - if (spec.perUserInstall) add("--win-per-user-install") - if (spec.shortcut) add("--win-shortcut") - spec.upgradeUuid?.let { add("--win-upgrade-uuid"); add(it) } - } - is MacOptions -> { - spec.packageIdentifier?.let { add("--mac-package-identifier"); add(it) } - spec.packageName?.let { add("--mac-package-name"); add(it) } - spec.bundleSigningPrefix?.let { add("--mac-bundle-signing-prefix"); add(it) } - if (spec.sign) add("--mac-sign") - spec.signingKeychain?.let { add("--mac-signing-keychain"); add(it.absolutePath) } - spec.signingKeyUserName?.let { add("--mac-signing-key-user-name"); add(it) } - } - is LinuxOptions -> { - spec.packageName?.let { add("--linux-package-name"); add(it) } - spec.debMaintainer?.let { add("--linux-deb-maintainer"); add(it) } - spec.menuGroup?.let { add("--linux-menu-group"); add(it) } - spec.packageDependencies?.let { add("--linux-package-deps"); add(it) } - spec.rpmLicenseType?.let { add("--linux-rpm-license-type"); add(it) } - spec.appRelease?.let { add("--linux-app-release"); add(it) } - spec.appCategory?.let { add("--linux-app-category"); add(it) } - if (spec.shortcut) add("--linux-shortcut") - } - } - } -} diff --git a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/internal/DefaultPackagingExtension.kt b/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/internal/DefaultPackagingExtension.kt deleted file mode 100644 index b17f8eb..0000000 --- a/packaging-gradle-plugin/src/main/kotlin/com/hendraanggrian/packaging/internal/DefaultPackagingExtension.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.hendraanggrian.packaging.internal - -import com.hendraanggrian.packaging.LinuxOptions -import com.hendraanggrian.packaging.LinuxOptionsImpl -import com.hendraanggrian.packaging.MacOptions -import com.hendraanggrian.packaging.MacOptionsImpl -import com.hendraanggrian.packaging.PackagingExtension -import com.hendraanggrian.packaging.WindowsOptions -import com.hendraanggrian.packaging.WindowsOptionsImpl -import org.gradle.api.Action -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.property -import org.gradle.kotlin.dsl.setProperty -import java.io.File - -open class DefaultPackagingExtension(private val objects: ObjectFactory, layout: ProjectLayout) : - PackagingExtension { - - var windowsOptions: WindowsOptions? = null - var macOptions: MacOptions? = null - var linuxOptions: LinuxOptions? = null - - override val windows: WindowsOptions - get() { - if (windowsOptions == null) { - windowsOptions = WindowsOptionsImpl(objects, this) - } - return windowsOptions!! - } - - override fun windows(action: Action): Unit = action.execute(windows) - - override val mac: MacOptions - get() { - if (macOptions == null) { - macOptions = MacOptionsImpl(objects, this) - } - return macOptions!! - } - - override fun mac(action: Action): Unit = action.execute(mac) - - override val linux: LinuxOptions - get() { - if (linuxOptions == null) { - linuxOptions = LinuxOptionsImpl(objects, this) - } - return linuxOptions!! - } - - override fun linux(action: Action): Unit = action.execute(linux) - - //region Generic Options - final override val appVersion: Property = objects.property() - - final override val copyright: Property = objects.property() - - final override val appDescription: Property = objects.property() - - final override val appName: Property = objects.property() - - final override val outputDirectory: DirectoryProperty = objects.directoryProperty() - .convention(layout.buildDirectory.dir("install")) - - final override val vendor: Property = objects.property() - - final override val verbose: Property = objects.property() - .convention(false) - //endregion - - //region Options for creating the runtime image - final override val modules: SetProperty = objects.setProperty() - - final override val modulePaths: SetProperty = objects.setProperty() - - final override val bindServices: Property = objects.property() - - final override val runtimeImage: RegularFileProperty = objects.fileProperty() - //endregion - - //region Options for creating the application image - final override val icon: RegularFileProperty = objects.fileProperty() - - final override val inputDirectory: DirectoryProperty = objects.directoryProperty() - //endregion - - //region Options for creating the application launcher(s) - final override val launcher: RegularFileProperty = objects.fileProperty() - - final override val args: ListProperty = objects.listProperty() - - final override val javaArgs: ListProperty = objects.listProperty() - - final override val mainClass: Property = objects.property() - - final override val mainJar: Property = objects.property() - - final override val mainModule: Property = objects.property() - //endregion - - //region Options for creating the application package - final override val appImage: RegularFileProperty = objects.fileProperty() - - final override val fileAssociations: RegularFileProperty = objects.fileProperty() - - final override val installDirectory: DirectoryProperty = objects.directoryProperty() - - final override val license: RegularFileProperty = objects.fileProperty() - - final override val resourcesDirectory: DirectoryProperty = objects.directoryProperty() - //endregion -} diff --git a/packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingFunctionalTest.kt b/packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingFunctionalTest.kt similarity index 75% rename from packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingFunctionalTest.kt rename to packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingFunctionalTest.kt index 033c985..3bd7873 100644 --- a/packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingFunctionalTest.kt +++ b/packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingFunctionalTest.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME import org.gradle.testkit.runner.GradleRunner @@ -22,34 +22,36 @@ class PackagingFunctionalTest { testProjectDir.newFile("settings.gradle.kts").writeText( """ rootProject.name = "functional-test" - """.trimIndent() + """.trimIndent(), ) buildFile = testProjectDir.newFile("build.gradle.kts") - runner = GradleRunner.create() - .withPluginClasspath() - .withProjectDir(testProjectDir.root) - .withTestKitDir(testProjectDir.newFolder()) + runner = + GradleRunner + .create() + .withPluginClasspath() + .withProjectDir(testProjectDir.root) + .withTestKitDir(testProjectDir.newFolder()) } - // TODO: fix NullPointerException + // TODO fix NullPointerException // @Test fun minimalConfiguration() { testProjectDir.newFolder("lib").resolve("sample.jar").createNewFile() buildFile.writeText( """ plugins { - id("com.hendraanggrian.packaging") + id("com.hanggrian.packaging") } packaging { mainJar.set("sample.jar") mainClass.set("com.example.App") inputDirectory.set(projectDir.resolve("lib")) } - """.trimIndent() + """.trimIndent(), ) assertEquals( UP_TO_DATE, - runner.withArguments(CHECK_TASK_NAME).build().task(":$CHECK_TASK_NAME")!!.outcome + runner.withArguments(CHECK_TASK_NAME).build().task(":$CHECK_TASK_NAME")!!.outcome, ) } } diff --git a/packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingIntegrationTest.kt b/packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingIntegrationTest.kt similarity index 78% rename from packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingIntegrationTest.kt rename to packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingIntegrationTest.kt index e54d48c..4f33dad 100644 --- a/packaging-gradle-plugin/src/test/kotlin/com/hendraanggrian/packaging/PackagingIntegrationTest.kt +++ b/packaging-gradle-plugin/src/test/kotlin/com/hanggrian/packaging/PackagingIntegrationTest.kt @@ -1,4 +1,4 @@ -package com.hendraanggrian.packaging +package com.hanggrian.packaging import org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME import org.gradle.testkit.runner.GradleRunner @@ -27,13 +27,15 @@ class PackagingIntegrationTest { testProjectDir.newFile("settings.gradle.kts").writeText( """ rootProject.name = "integration-test" - """.trimIndent() + """.trimIndent(), ) buildFile = testProjectDir.newFile("build.gradle.kts") - runner = GradleRunner.create() - .withPluginClasspath() - .withProjectDir(testProjectDir.root) - .withTestKitDir(testProjectDir.newFolder()) + runner = + GradleRunner + .create() + .withPluginClasspath() + .withProjectDir(testProjectDir.root) + .withTestKitDir(testProjectDir.newFolder()) } @Test @@ -42,17 +44,17 @@ class PackagingIntegrationTest { """ plugins { application - id("com.hendraanggrian.packaging") + id("com.hanggrian.packaging") } application { applicationName = "MyApp" mainClass.set("com.example.App") } - """.trimIndent() + """.trimIndent(), ) assertEquals( UP_TO_DATE, - runner.withArguments(CHECK_TASK_NAME).build().task(":$CHECK_TASK_NAME")!!.outcome + runner.withArguments(CHECK_TASK_NAME).build().task(":$CHECK_TASK_NAME")!!.outcome, ) } } diff --git a/samples/custom-dir/build.gradle b/samples/custom-dir/build.gradle index c29a33c..89acf00 100644 --- a/samples/custom-dir/build.gradle +++ b/samples/custom-dir/build.gradle @@ -1,21 +1,33 @@ buildscript { - repositories.mavenCentral() + repositories { + gradlePluginPortal() + mavenCentral() + } dependencies { - classpath files('../packaging-gradle-plugin-0.2.jar') + classpath files('../packaging-gradle-plugin-0.1.jar') classpath libs.osdetector + classpath 'org.openjfx:javafx-plugin:0.1.0' } } group 'com.example' version '1.0' -apply plugin: 'com.hendraanggrian.packaging' +apply plugin: 'java' +apply plugin: 'com.hanggrian.packaging' +apply plugin: 'org.openjfx.javafxplugin' + +javafx { + version = "21.0.4" + modules 'javafx.controls', 'javafx.graphics' +} packaging { appName.set('Custom Directory') mainClass.set('com.example.App') outputDirectory.set(new File(buildDir, 'awesome')) inputDirectory.set(new File(buildDir, 'libs')) - modules = ['javafx.controls'] + modules = ['javafx.controls', 'javafx.graphics'] + modulePaths = [file('~/SDK/javafx-sdk-21.0.4/lib')] verbose.set(true) } diff --git a/samples/custom-dir/src/main/java/com/example/App.java b/samples/custom-dir/src/main/java/com/example/App.java index d197c04..4c52335 100644 --- a/samples/custom-dir/src/main/java/com/example/App.java +++ b/samples/custom-dir/src/main/java/com/example/App.java @@ -6,8 +6,7 @@ import javafx.stage.Stage; public class App extends Application { - - public static void main(String... args) { + public static void main(String[] args) { launch(args); } diff --git a/samples/icon/build.gradle b/samples/icon/build.gradle index 529086b..4cf6e97 100644 --- a/samples/icon/build.gradle +++ b/samples/icon/build.gradle @@ -1,21 +1,33 @@ buildscript { - repositories.mavenCentral() + repositories { + gradlePluginPortal() + mavenCentral() + } dependencies { - classpath files('../packaging-gradle-plugin-0.2.jar') + classpath files('../packaging-gradle-plugin-0.1.jar') classpath libs.osdetector + classpath 'org.openjfx:javafx-plugin:0.1.0' } } group 'com.example' version '1.0' -apply plugin: 'com.hendraanggrian.packaging' +apply plugin: 'java' +apply plugin: 'com.hanggrian.packaging' +apply plugin: 'org.openjfx.javafxplugin' + +javafx { + version = "21.0.4" + modules 'javafx.controls', 'javafx.graphics' +} packaging { appName.set('Icon') icon.set(new File(projectDir, 'art/example.icns')) mainClass.set('com.example.App') inputDirectory.set(new File(buildDir, 'libs')) - modules = ['javafx.controls'] + modules = ['javafx.controls', 'javafx.graphics'] + modulePaths = [file('~/SDK/javafx-sdk-21.0.4/lib')] verbose.set(true) } diff --git a/samples/icon/src/main/java/com/example/App.java b/samples/icon/src/main/java/com/example/App.java index d197c04..4c52335 100644 --- a/samples/icon/src/main/java/com/example/App.java +++ b/samples/icon/src/main/java/com/example/App.java @@ -6,8 +6,7 @@ import javafx.stage.Stage; public class App extends Application { - - public static void main(String... args) { + public static void main(String[] args) { launch(args); } diff --git a/samples/packaging-gradle-plugin-0.1.jar b/samples/packaging-gradle-plugin-0.1.jar new file mode 100644 index 0000000..3301715 Binary files /dev/null and b/samples/packaging-gradle-plugin-0.1.jar differ diff --git a/samples/packaging-gradle-plugin-0.2.jar b/samples/packaging-gradle-plugin-0.2.jar deleted file mode 100644 index 4110c89..0000000 Binary files a/samples/packaging-gradle-plugin-0.2.jar and /dev/null differ diff --git a/samples/with-application-plugin/build.gradle b/samples/with-application-plugin/build.gradle index 6066921..a32ae1b 100644 --- a/samples/with-application-plugin/build.gradle +++ b/samples/with-application-plugin/build.gradle @@ -1,8 +1,12 @@ buildscript { - repositories.mavenCentral() + repositories { + gradlePluginPortal() + mavenCentral() + } dependencies { - classpath files('../packaging-gradle-plugin-0.2.jar') + classpath files('../packaging-gradle-plugin-0.1.jar') classpath libs.osdetector + classpath 'org.openjfx:javafx-plugin:0.1.0' } } @@ -10,17 +14,26 @@ group 'com.example' version '1.0' apply plugin: 'application' -apply plugin: 'com.hendraanggrian.packaging' +apply plugin: 'com.hanggrian.packaging' +apply plugin: 'org.openjfx.javafxplugin' + +javafx { + version = "21.0.4" + modules 'javafx.controls', 'javafx.graphics' +} application { applicationName = 'My App' mainClass.set('com.example.App') applicationDefaultJvmArgs = [ - '--add-modules=javafx.controls' + '--add-modules=javafx.controls', + '--add-modules=javafx.graphics', ] } packaging { - modules = ['javafx.controls'] + modules = ['javafx.controls', 'javafx.graphics'] + modulePaths = [file('~/SDK/javafx-sdk-21.0.4/lib')] + verbose.set(true) } diff --git a/samples/with-application-plugin/src/main/java/com/example/App.java b/samples/with-application-plugin/src/main/java/com/example/App.java index d197c04..4c52335 100644 --- a/samples/with-application-plugin/src/main/java/com/example/App.java +++ b/samples/with-application-plugin/src/main/java/com/example/App.java @@ -6,8 +6,7 @@ import javafx.stage.Stage; public class App extends Application { - - public static void main(String... args) { + public static void main(String[] args) { launch(args); } diff --git a/settings.gradle.kts b/settings.gradle.kts index fa83262..60b7304 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,10 +2,7 @@ pluginManagement.repositories { gradlePluginPortal() mavenCentral() } -dependencyResolutionManagement.repositories { - mavenCentral() - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") -} +dependencyResolutionManagement.repositories.mavenCentral() rootProject.name = "packaging-gradle-plugin" @@ -13,7 +10,8 @@ include("packaging-gradle-plugin") include("website") includeDir("samples") // enabling samples will break Travis because JavaFX SDK is no longer free to download -fun includeDir(dir: String) = file(dir) - .listFiles()!! - .filter { it.isDirectory } - .forEach { include("$dir:${it.name}") } +fun includeDir(dir: String) = + file(dir) + .listFiles()!! + .filter { it.isDirectory } + .forEach { include("$dir:${it.name}") } diff --git a/website/build.gradle.kts b/website/build.gradle.kts index 2fab647..66903c8 100644 --- a/website/build.gradle.kts +++ b/website/build.gradle.kts @@ -1,36 +1,43 @@ +val developerId: String by project +val developerName: String by project +val developerUrl: String by project +val releaseArtifact: String by project +val releaseDescription: String by project +val releaseUrl: String by project + plugins { alias(libs.plugins.pages) alias(libs.plugins.git.publish) } pages { - resources.from("src", "$rootDir/$RELEASE_ARTIFACT/build/dokka/") - styles.add("styles/prism-tomorrow.min.css") + resources.from("src", "$rootDir/$releaseArtifact/build/dokka/") + styles.add("styles/prism-tomorrow.css") scripts.addAll( "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-groovy.min.js" + "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-gradle.min.js", ) minimal { - authorName = DEVELOPER_NAME - authorUrl = DEVELOPER_URL - projectName = RELEASE_ARTIFACT - projectDescription = RELEASE_DESCRIPTION - projectUrl = RELEASE_URL + authorName = developerName + authorUrl = developerUrl + projectName = releaseArtifact + projectDescription = releaseDescription + projectUrl = releaseUrl button("View\nDocumentation", "dokka") } } gitPublish { - repoUri.set("git@github.com:$DEVELOPER_ID/$RELEASE_ARTIFACT.git") + repoUri.set("git@github.com:$developerId/$releaseArtifact.git") branch.set("gh-pages") contents.from(pages.outputDirectory) } tasks { register(LifecycleBasePlugin.CLEAN_TASK_NAME) { - delete(buildDir) + delete(layout.buildDirectory) } - deployPages { - dependsOn(":$RELEASE_ARTIFACT:dokkaHtml") + deployResources { + dependsOn(":$releaseArtifact:dokkaHtml") } } diff --git a/website/src/styles/prism-tomorrow.css b/website/src/styles/prism-tomorrow.css new file mode 100644 index 0000000..81c4d8d --- /dev/null +++ b/website/src/styles/prism-tomorrow.css @@ -0,0 +1,118 @@ +/** Modified to disable margin below code blocks. */ + +code[class*="language-"], +pre[class*="language-"] { + color: #ccc; + background: none; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + /* margin: .5em 0; */ + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #2d2d2d; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.block-comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999; +} + +.token.punctuation { + color: #ccc; +} + +.token.tag, +.token.attr-name, +.token.namespace, +.token.deleted { + color: #e2777a; +} + +.token.function-name { + color: #6196cc; +} + +.token.boolean, +.token.number, +.token.function { + color: #f08d49; +} + +.token.property, +.token.class-name, +.token.constant, +.token.symbol { + color: #f8c555; +} + +.token.selector, +.token.important, +.token.atrule, +.token.keyword, +.token.builtin { + color: #cc99cd; +} + +.token.string, +.token.char, +.token.attr-value, +.token.regex, +.token.variable { + color: #7ec699; +} + +.token.operator, +.token.entity, +.token.url { + color: #67cdcc; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.inserted { + color: green; +} diff --git a/website/src/styles/prism-tomorrow.min.css b/website/src/styles/prism-tomorrow.min.css deleted file mode 100644 index 24e35dd..0000000 --- a/website/src/styles/prism-tomorrow.min.css +++ /dev/null @@ -1 +0,0 @@ -code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}