diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6e007c2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 diff --git a/.github/workflows/flow-deploy-release-artifact.yaml b/.github/workflows/flow-deploy-release-artifact.yaml new file mode 100644 index 0000000..f53ac8e --- /dev/null +++ b/.github/workflows/flow-deploy-release-artifact.yaml @@ -0,0 +1,40 @@ +name: "Deploy Release Artifact" +on: + workflow_dispatch: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+-?*" + +defaults: + run: + shell: bash + +env: + LC_ALL: C.UTF-8 + GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }} + GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }} + +jobs: + plugin-portal-release: + name: Release Gradle Plugin Portal + runs-on: network-node-linux-medium + steps: + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + with: + distribution: temurin + java-version: 17.0.12 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + + - name: Gradle Plugin Portal Release + env: + GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} + GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} + run: ./gradlew publishPlugins -PpublishSigningEnabled=true --no-configuration-cache --scan diff --git a/.github/workflows/flow-pull-request-checks.yaml b/.github/workflows/flow-pull-request-checks.yaml new file mode 100644 index 0000000..a81123d --- /dev/null +++ b/.github/workflows/flow-pull-request-checks.yaml @@ -0,0 +1,24 @@ +name: "PR Checks" +on: + workflow_dispatch: + pull_request: + types: + - opened + - reopened + - synchronize + push: + branches: + - main + +defaults: + run: + shell: bash + +concurrency: + group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + name: Code + uses: ./.github/workflows/zxc-build-plugins.yaml diff --git a/.github/workflows/zxc-build-plugins.yaml b/.github/workflows/zxc-build-plugins.yaml new file mode 100644 index 0000000..95ab419 --- /dev/null +++ b/.github/workflows/zxc-build-plugins.yaml @@ -0,0 +1,54 @@ +name: "ZXC: Compile" +on: + workflow_call: +# secrets: +# gradle-cache-username: +# description: "The username used to authenticate with the Gradle Build Cache Node." +# required: true +# gradle-cache-password: +# description: "The password used to authenticate with the Gradle Build Cache Node." +# required: true + +defaults: + run: + shell: bash + +env: + LC_ALL: C.UTF-8 + GRADLE_CACHE_USERNAME: ${{ secrets.gradle-cache-username }} + GRADLE_CACHE_PASSWORD: ${{ secrets.gradle-cache-password }} + +jobs: + compile: + name: Compile and Test +# runs-on: network-node-linux-medium + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 + with: + distribution: temurin + java-version: 17.0.12 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2 + with: + cache-read-only: false + + - name: Compile Code + run: ./gradlew assemble --scan + + - name: Code Quality Checks and Tests + run: ./gradlew check --scan + +# - name: Publish JUnit Test Report +# uses: step-security/publish-unit-test-result-action@4519d7c9f71dd765f8bbb98626268780f23bab28 # v2.17.0 +# with: +# check_name: JUnit Test Report +# time_unit: seconds +# junit_files: "build/test-results/**/*.xml" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0d53d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.gradle +.idea +build diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..62726f4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Hedera Gradle Conventions - Changelog + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a92acd7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Hedera Gradle Conventions + +Gradle convention plugins used by Hiero projects. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..c9cfb91 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + `kotlin-dsl` + id("signing") + id("com.gradle.plugin-publish") version "1.3.0" + id("com.diffplug.spotless") version "6.25.0" +} + +version = "0.0.5" + +group = "org.hiero" + +description = "Gradle convention plugins used by Hiero projects" + +java { toolchain.languageVersion = JavaLanguageVersion.of(17) } + +dependencies { + implementation("com.adarshr:gradle-test-logger-plugin:4.0.0") + implementation("com.autonomousapps:dependency-analysis-gradle-plugin:2.4.2") + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.25.0") + implementation("com.google.protobuf:protobuf-gradle-plugin:0.9.4") + implementation("com.gradle:develocity-gradle-plugin:3.18.1") + implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") + implementation( + "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.2" + ) + implementation("io.github.gradle-nexus:publish-plugin:1.3.0") + implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.2") + implementation("net.swiftzer.semver:semver:2.0.0") + implementation("org.gradlex:extra-java-module-info:1.9") + implementation("org.gradlex:java-module-dependencies:1.8") + implementation("org.gradlex:jvm-dependency-conflict-resolution:2.1.2") + implementation("org.gradlex:reproducible-builds:1.0") +} + +gradlePlugin { + website = "https://github.com/hiero-ledger/hiero-gradle-conventions" + vcsUrl = "https://github.com/hiero-ledger/hiero-gradle-conventions" + plugins.configureEach { + description = project.description + @Suppress("UnstableApiUsage") + tags = listOf("conventions", "java", "modules", "jpms") + } + + plugins.configureEach { displayName = name } +} + +publishing.publications.withType().configureEach { + pom { + url = "https://hiero.org/" + inceptionYear = "2024" + description = project.description + organization { + name = "Hiero - a Linux Foundation Decentralized Trust project" + url = "https://hiero.org/" + } + + val repoName = project.name + issueManagement { + system = "GitHub" + url = "https://github.com/hiero-ledger/$repoName/issues" + } + + licenses { + license { + name = "Apache License, Version 2.0" + url = "https://raw.githubusercontent.com/hiero-ledger/$repoName/main/LICENSE" + } + } + + scm { + connection = "scm:git:git://github.com/hiero-ledger/$repoName.git" + developerConnection = "scm:git:ssh://github.com:hiero-ledger/$repoName.git" + url = "https://github.com/hiero-ledger/$repoName" + } + + developers { + developer { + name = "Release Engineering Team" + email = "release-engineering@hiero.org" + organization = "Hiero - a Linux Foundation Decentralized Trust project" + organizationUrl = "https://hiero.org/" + } + } + } +} + +val publishSigningEnabled = + providers.gradleProperty("publishSigningEnabled").getOrElse("false").toBoolean() + +if (publishSigningEnabled) { + signing { + useInMemoryPgpKeys( + providers.environmentVariable("SIGNING_KEY").get(), + providers.environmentVariable("SIGNING_PASSPHRASE").get() + ) + } +} + +spotless { + val header = + """ + /* + * Copyright (C) ${'$'}YEAR Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */${"\n\n"} + """ + .trimIndent() + val top = + "(import|package|plugins|pluginManagement|dependencyResolutionManagement|repositories|tasks|allprojects|subprojects|buildCache|version)" + + kotlinGradle { + ktfmt().kotlinlangStyle() + licenseHeader(header, top).updateYearWithLatest(true) + } + kotlin { + ktfmt().kotlinlangStyle() + targetExclude("build/**") + licenseHeader(header, top).updateYearWithLatest(true) + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..d39edd1 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..df97d72 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..f5feea6 --- /dev/null +++ b/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# 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/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# 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 + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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. +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=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +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, 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" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9b42019 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@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 ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +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 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +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 + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..5dd875d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.gradle.develocity") version "3.18.1" } + +rootProject.name = "hiero-gradle-conventions" + +develocity { + buildScan { + termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" + termsOfUseAgree = "yes" + // Enable Gradle Build Scan only with explicit '--scan' + publishing.onlyIf { false } + } +} + +dependencyResolutionManagement { @Suppress("UnstableApiUsage") repositories.gradlePluginPortal() } diff --git a/src/main/kotlin/org.hiero.gradle.base.jpms-modules.gradle.kts b/src/main/kotlin/org.hiero.gradle.base.jpms-modules.gradle.kts new file mode 100644 index 0000000..ad7bf3f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.base.jpms-modules.gradle.kts @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("org.gradlex.jvm-dependency-conflict-resolution") + id("org.gradlex.extra-java-module-info") +} + +// Fix or enhance the metadata of third-party Modules. This is about the metadata in the +// repositories: '*.pom' and '*.module' files. +jvmDependencyConflicts.patch { + // These compile time annotation libraries are not of interest in our setup and are thus removed + // from the dependencies of all components that bring them in. + val annotationLibraries = + listOf( + "com.google.android:annotations", + "com.google.code.findbugs:annotations", + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:listenablefuture", + "org.checkerframework:checker-compat-qual", + "org.checkerframework:checker-qual", + "org.codehaus.mojo:animal-sniffer-annotations" + ) + + module("io.netty:netty-transport-native-epoll") { + addFeature("linux-x86_64") + addFeature("linux-aarch_64") + } + module("io.grpc:grpc-api") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-core") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-context") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-netty") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-protobuf") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-protobuf-lite") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-services") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-stub") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-testing") { annotationLibraries.forEach { removeDependency(it) } } + module("io.grpc:grpc-util") { annotationLibraries.forEach { removeDependency(it) } } + module("com.github.ben-manes.caffeine:caffeine") { + annotationLibraries.forEach { removeDependency(it) } + } + module("com.google.dagger:dagger-compiler") { + annotationLibraries.forEach { removeDependency(it) } + } + module("com.google.dagger:dagger-producers") { + annotationLibraries.forEach { removeDependency(it) } + } + module("com.google.dagger:dagger-spi") { annotationLibraries.forEach { removeDependency(it) } } + module("com.google.guava:guava") { + (annotationLibraries - + "com.google.code.findbugs:jsr305" - + "com.google.errorprone:error_prone_annotations" - + "org.checkerframework:checker-qual") + .forEach { removeDependency(it) } + } + module("com.google.protobuf:protobuf-java-util") { + annotationLibraries.forEach { removeDependency(it) } + } + module("org.apache.tuweni:tuweni-bytes") { removeDependency("com.google.code.findbugs:jsr305") } + module("org.apache.tuweni:tuweni-units") { removeDependency("com.google.code.findbugs:jsr305") } + module("io.prometheus:simpleclient") { + removeDependency("io.prometheus:simpleclient_tracer_otel") + removeDependency("io.prometheus:simpleclient_tracer_otel_agent") + } + module("org.jetbrains.kotlin:kotlin-stdlib") { + removeDependency("org.jetbrains.kotlin:kotlin-stdlib-common") + } + module("junit:junit") { removeDependency("org.hamcrest:hamcrest-core") } + module("org.hyperledger.besu:secp256k1") { addApiDependency("net.java.dev.jna:jna") } +} + +// Fix or enhance the 'module-info.class' of third-party Modules. This is about the +// 'module-info.class' inside the Jar files. In our full Java Modules setup every +// Jar needs to have this file. If it is missing, it is added by what is configured here. +extraJavaModuleInfo { + failOnAutomaticModules = true // Only allow Jars with 'module-info' on all module paths + versionsProvidingConfiguration = "mainRuntimeClasspath" + + module("io.grpc:grpc-api", "io.grpc") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + uses("io.grpc.LoadBalancerProvider") + uses("io.grpc.ManagedChannelProvider") + uses("io.grpc.NameResolverProvider") + } + module("io.grpc:grpc-core", "io.grpc.internal") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + } + module("io.grpc:grpc-context", "io.grpc.context") + module("io.grpc:grpc-inprocess", "io.grpc.inprocess") + module("io.grpc:grpc-netty", "io.grpc.netty") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + } + module("io.grpc:grpc-stub", "io.grpc.stub") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + } + module("io.grpc:grpc-util", "io.grpc.util") + module("io.grpc:grpc-protobuf", "io.grpc.protobuf") + module("io.grpc:grpc-protobuf-lite", "io.grpc.protobuf.lite") + module("com.github.spotbugs:spotbugs-annotations", "com.github.spotbugs.annotations") + module("com.google.code.findbugs:jsr305", "java.annotation") + module("com.google.protobuf:protobuf-javalite", "com.google.protobuf") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + requires("jdk.unsupported") + } + module("com.google.protobuf:protobuf-java", "com.google.protobuf") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + requires("jdk.unsupported") + } + module("com.google.guava:guava", "com.google.common") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + } + module("com.google.guava:failureaccess", "com.google.common.util.concurrent.internal") + module("com.google.api.grpc:proto-google-common-protos", "com.google.api.grpc.common") + module("com.google.dagger:dagger", "dagger") + module("io.perfmark:perfmark-api", "io.perfmark") + module("javax.inject:javax.inject", "javax.inject") + module("commons-codec:commons-codec", "org.apache.commons.codec") + module("com.esaulpaugh:headlong", "headlong") + module("org.connid:framework", "org.connid.framework") + module("org.connid:framework-internal", "org.connid.framework.internal") { + exportAllPackages() + requires("org.connid.framework") // this is missing in POM + } + module("org.jetbrains:annotations", "org.jetbrains.annotations") + module("io.tmio:tuweni-units", "tuweni.units") + module("io.tmio:tuweni-bytes", "tuweni.bytes") + module("net.i2p.crypto:eddsa", "net.i2p.crypto.eddsa") + module("io.netty:netty-codec-http", "io.netty.codec.http") + module("io.netty:netty-codec-http2", "io.netty.codec.http2") + module("io.netty:netty-codec-socks", "io.netty.codec.socks") + module("io.netty:netty-handler-proxy", "io.netty.handler.proxy") + module("io.netty:netty-transport-native-unix-common", "io.netty.transport.unix.common") + module("io.netty:netty-buffer", "io.netty.buffer") + module("io.netty:netty-codec", "io.netty.codec") + module("io.netty:netty-common", "io.netty.common") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + requires("jdk.unsupported") + ignoreServiceProvider("reactor.blockhound.integration.BlockHoundIntegration") + } + module("io.netty:netty-handler", "io.netty.handler") + module("io.netty:netty-resolver", "io.netty.resolver") + module("io.netty:netty-transport", "io.netty.transport") + module("io.netty:netty-transport-classes-epoll", "io.netty.transport.classes.epoll") + module("org.antlr:antlr4-runtime", "org.antlr.antlr4.runtime") + module("org.hyperledger.besu.internal:algorithms", "org.hyperledger.besu.internal.crypto") + module("org.hyperledger.besu.internal:rlp", "org.hyperledger.besu.internal.rlp") + module("org.hyperledger.besu:arithmetic", "org.hyperledger.besu.nativelib.arithmetic") + module("org.hyperledger.besu:blake2bf", "org.hyperledger.besu.nativelib.blake2bf") + module("org.hyperledger.besu:bls12-381", "org.hyperledger.besu.nativelib.bls12_381") + module("org.hyperledger.besu:besu-datatypes", "org.hyperledger.besu.datatypes") + module("org.hyperledger.besu:evm", "org.hyperledger.besu.evm") { + exportAllPackages() + requireAllDefinedDependencies() + requiresStatic("com.fasterxml.jackson.annotation") + } + module("org.hyperledger.besu:secp256k1", "org.hyperledger.besu.nativelib.secp256k1") + module("org.hyperledger.besu:secp256r1", "org.hyperledger.besu.nativelib.secp256r1") + module("com.goterl:resource-loader", "resource.loader") + module("com.goterl:lazysodium-java", "lazysodium.java") + module("tech.pegasys:jc-kzg-4844", "tech.pegasys.jckzg4844") + module("net.java.dev.jna:jna", "com.sun.jna") { + exportAllPackages() + requires("java.logging") + } + module("org.eclipse.collections:eclipse-collections-api", "org.eclipse.collections.api") + module("org.eclipse.collections:eclipse-collections", "org.eclipse.collections.impl") + module("io.prometheus:simpleclient", "io.prometheus.simpleclient") + module("io.prometheus:simpleclient_common", "io.prometheus.simpleclient_common") + module("io.prometheus:simpleclient_httpserver", "io.prometheus.simpleclient.httpserver") { + exportAllPackages() + requireAllDefinedDependencies() + requires("jdk.httpserver") + } + + // Need to use Jar file names here as there is currently no other way to address Jar with + // classifier directly for patching + module( + "io.netty:netty-transport-native-epoll|linux-x86_64", + "io.netty.transport.epoll.linux.x86_64" + ) + module( + "io.netty:netty-transport-native-epoll|linux-aarch_64", + "io.netty.transport.epoll.linux.aarch_64" + ) + + // Annotation processing only + module("com.google.auto.service:auto-service-annotations", "com.google.auto.service") + module("com.google.auto.service:auto-service", "com.google.auto.service.processor") + module("com.google.auto:auto-common", "com.google.auto.common") + module("com.google.dagger:dagger-compiler", "dagger.compiler") + module("com.google.dagger:dagger-producers", "dagger.producers") + module("com.google.dagger:dagger-spi", "dagger.spi") + module( + "com.google.devtools.ksp:symbol-processing-api", + "com.google.devtools.ksp.symbolprocessingapi" + ) + module("com.google.errorprone:javac-shaded", "com.google.errorprone.javac.shaded") + module("com.google.googlejavaformat:google-java-format", "com.google.googlejavaformat") + module("net.ltgt.gradle.incap:incap", "net.ltgt.gradle.incap") + module("org.jetbrains.kotlinx:kotlinx-metadata-jvm", "kotlinx.metadata.jvm") + + // Annotation processing - error prone + module( + "com.github.kevinstern:software-and-algorithms", + "com.github.kevinstern.software_and_algorithms" + ) + module("com.google.auto.value:auto-value-annotations", "com.google.auto.value.annotations") + module("com.google.errorprone:error_prone_annotation", "com.google.errorprone.annotation") + module("com.google.errorprone:error_prone_check_api", "com.google.errorprone.check.api") + module("com.google.errorprone:error_prone_core", "com.google.errorprone.core") + module( + "com.google.errorprone:error_prone_type_annotations", + "com.google.errorprone.type.annotations" + ) + module("com.uber.nullaway:nullaway", "com.uber.nullaway") + module("io.github.eisop:dataflow-errorprone", "org.checkerframework.dataflow") + module("io.github.java-diff-utils:java-diff-utils", "io.github.javadiffutils") + module("io.grpc:grpc-java-api-checker", "io.grpc.java.api.checker") + + // Testing only + module("io.grpc:grpc-netty-shaded", "io.grpc.netty.shaded") { + exportAllPackages() + requireAllDefinedDependencies() + requires("java.logging") + requires("jdk.unsupported") + ignoreServiceProvider("reactor.blockhound.integration.BlockHoundIntegration") + } + module("com.google.jimfs:jimfs", "com.google.jimfs") + module("io.github.json-snapshot:json-snapshot", "json.snapshot") + module("org.awaitility:awaitility", "awaitility") + module("uk.org.webcompere:system-stubs-core", "uk.org.webcompere.systemstubs.core") + module("uk.org.webcompere:system-stubs-jupiter", "uk.org.webcompere.systemstubs.jupiter") + + // Testing only + module("com.github.docker-java:docker-java-api", "com.github.dockerjava.api") + module("com.github.docker-java:docker-java-transport", "com.github.dockerjava.transport") + module( + "com.github.docker-java:docker-java-transport-zerodep", + "com.github.dockerjava.transport.zerodep" + ) + module("com.google.protobuf:protobuf-java-util", "com.google.protobuf.util") + module("com.squareup:javapoet", "com.squareup.javapoet") { + exportAllPackages() + requires("java.compiler") + } + module("junit:junit", "junit") + module("org.hamcrest:hamcrest", "org.hamcrest") + module("org.json:json", "org.json") + module("org.mockito:mockito-core", "org.mockito") + module("org.objenesis:objenesis", "org.objenesis") + module("org.rnorth.duct-tape:duct-tape", "org.rnorth.ducttape") + module("org.testcontainers:testcontainers", "org.testcontainers") + module("org.testcontainers:junit-jupiter", "org.testcontainers.junit.jupiter") + module("org.mockito:mockito-junit-jupiter", "org.mockito.junit.jupiter") +} + +// Configure consistent resolution across the whole project +val consistentResolutionAttribute = Attribute.of("consistent-resolution", String::class.java) + +configurations.create("allDependencies") { + isCanBeConsumed = true + isCanBeResolved = false + sourceSets.all { + extendsFrom( + configurations[this.implementationConfigurationName], + configurations[this.compileOnlyConfigurationName], + configurations[this.runtimeOnlyConfigurationName], + configurations[this.annotationProcessorConfigurationName] + ) + } + attributes { + attribute(consistentResolutionAttribute, "global") + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + } +} + +jvmDependencyConflicts.consistentResolution { + if (project == rootProject) { + // single project build, e.g. for examples + providesVersions(project.path) + } else { + providesVersions(":aggregation") + platform(":hedera-dependency-versions") + } +} + +configurations.getByName("mainRuntimeClasspath") { + attributes.attribute(consistentResolutionAttribute, "global") +} + +// In case published versions of a module are also available, always prefer the local one +configurations.all { resolutionStrategy.preferProjectModules() } diff --git a/src/main/kotlin/org.hiero.gradle.base.lifecycle.gradle.kts b/src/main/kotlin/org.hiero.gradle.base.lifecycle.gradle.kts new file mode 100644 index 0000000..0e75ece --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.base.lifecycle.gradle.kts @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("base") } + +// Convenience for local development: when running './gradlew' without any parameters show the tasks +// from the 'build' group +defaultTasks("tasks") + +tasks.register("qualityCheck") { + group = "verification" + description = "Run all spotless and quality checks." + dependsOn(tasks.assemble) +} + +tasks.register("qualityGate") { + group = "build" + description = "Apply spotless rules and run all quality checks." + dependsOn(tasks.assemble) +} + +tasks.check { dependsOn(tasks.named("qualityCheck")) } + +tasks.register("releaseMavenCentral") { group = "release" } + +afterEvaluate { + tasks.configureEach { + if (name in listOf("buildDependents", "buildNeeded", "classes")) { + group = null + } + if (name.endsWith("Classes")) { + group = null + } + if (this is Jar) { + setGroup(null) + } + if (this is Test) { + group = "build" + } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.base.version.gradle.kts b/src/main/kotlin/org.hiero.gradle.base.version.gradle.kts new file mode 100644 index 0000000..72c8368 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.base.version.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +version = + providers + .fileContents(isolated.rootProject.projectDirectory.file("version.txt")) + .asText + .orElse( + provider { + if (project.parent == null) { + "" + } else { + throw RuntimeException("version.txt file not found") + } + } + ) + .get() + .trim() diff --git a/src/main/kotlin/org.hiero.gradle.build.settings.gradle.kts b/src/main/kotlin/org.hiero.gradle.build.settings.gradle.kts new file mode 100644 index 0000000..d338299 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.build.settings.gradle.kts @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.gradlex.javamodule.dependencies.initialization.JavaModulesExtension +import org.gradlex.javamodule.dependencies.initialization.RootPluginsExtension + +plugins { + id("org.gradlex.java-module-dependencies") + id("org.hiero.gradle.feature.build-cache") + id("org.hiero.gradle.feature.repositories") + id("org.hiero.gradle.report.develocity") +} + +configure { + // Global plugins, that are applied to the "root project" instead of "settings". + // Having this here, we do not require a "build.gradle.kts" in the repository roots. + id("org.hiero.gradle.base.lifecycle") + id("org.hiero.gradle.feature.publish-maven-central.root") + id("org.hiero.gradle.feature.versioning") + id("org.hiero.gradle.check.spotless") + id("org.hiero.gradle.check.spotless-kotlin") + id("org.hiero.gradle.check.spotless-markdown") + id("org.hiero.gradle.check.spotless-misc") + id("org.hiero.gradle.check.spotless-yaml") +} + +// Allow projects inside a build to be addressed by dependency coordinates notation. +// https://docs.gradle.org/current/userguide/composite_builds.html#included_build_declaring_substitutions +// Some functionality of the 'java-module-dependencies' plugin relies on this. +includeBuild(".") + +@Suppress("UnstableApiUsage") +configure { + if (layout.rootDirectory.dir("gradle/aggregation").asFile.isDirectory) { + // Project to aggregate code coverage data for the whole repository into one report + module("gradle/aggregation") { + plugin("java") + plugin("org.hiero.gradle.base.jpms-modules") + } + } + if (layout.rootDirectory.dir("hedera-dependency-versions").asFile.isDirectory) { + // "BOM" with versions of 3rd party dependencies + versions("hedera-dependency-versions") + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.dependencies.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.dependencies.gradle.kts new file mode 100644 index 0000000..7037312 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.dependencies.gradle.kts @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.autonomousapps.DependencyAnalysisExtension +import com.autonomousapps.DependencyAnalysisSubExtension +import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesOrderingCheck +import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesScopeCheck + +plugins { + id("com.autonomousapps.dependency-analysis") + id("org.hiero.gradle.base.lifecycle") + id("org.hiero.gradle.base.jpms-modules") +} + +// ordering check is done by SortModuleInfoRequiresStep +tasks.withType { enabled = false } + +// Do not report dependencies from one source set to another as 'required'. +// In particular, in case of test fixtures, the analysis would suggest to +// add as testModuleInfo { require(...) } to the main module. This is +// conceptually wrong, because in whitebox testing the 'main' and 'test' +// module are conceptually considered one module (main module extended with tests) +if (project.parent == null) { + configure { issues { all { onAny { exclude(project.path) } } } } +} else { + configure { issues { onAny { exclude(project.path) } } } +} + +tasks.named("qualityCheck") { dependsOn(tasks.withType()) } + +tasks.named("qualityGate") { dependsOn(tasks.withType()) } diff --git a/src/main/kotlin/org.hiero.gradle.check.javac-lint.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.javac-lint.gradle.kts new file mode 100644 index 0000000..7f9c3e1 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.javac-lint.gradle.kts @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +val deactivatedCompileLintOptions = + listOf( + // In Gradle, a module does not see the upstream (not-yet-compiled) modules. This could + // only be solved by calling 'javac' with '--source-module-path' to make other sources + // known. But this is at odds with how Gradle's incremental compilation calls the + // compiler for a subset of Java files for each project individually. + "module", // module not found when doing 'exports to ...' + "serial", // serializable class ... has no definition of serialVersionUID + "processing", // No processor claimed any of these annotations: ... + "try", // auto-closeable resource ignore is never referenced... (AutoClosableLock) + "missing-explicit-ctor", // class ... declares no explicit constructors + + // Needed because we use deprecation internally and do not fix all uses right away + "removal", + "deprecation", + + // The following checks could be activated and fixed: + "overrides", // overrides equals, but neither it ... overrides hashCode method + "unchecked", + "rawtypes" + ) + +val deactivatedCompileLintOptionsJava21 = + listOf( + "this-escape", // calling public/protected method in constructor + ) + +tasks.withType().configureEach { + options.compilerArgs.add("-implicit:none") + options.compilerArgs.add("-Werror") + options.compilerArgs.add("-Xlint:all") + options.compilerArgs.add("-Xlint:-" + deactivatedCompileLintOptions.joinToString(",-")) + if (java.targetCompatibility >= JavaVersion.VERSION_21) { + options.compilerArgs.add( + "-Xlint:-" + deactivatedCompileLintOptionsJava21.joinToString(",-") + ) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless-java.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless-java.gradle.kts new file mode 100644 index 0000000..8ba5fb4 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless-java.gradle.kts @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.hiero.gradle.spotless.RepairDashedCommentsFormatterStep +import org.hiero.gradle.spotless.SortModuleInfoRequiresStep + +plugins { id("com.diffplug.spotless") } + +spotless { + java { + targetExclude("build/generated/sources/**/*.java", "build/generated/source/**/*.java") + + // fix errors due to dashed comment blocks (eg: /*-, /*--, etc) + addStep(RepairDashedCommentsFormatterStep.create()) + // Sort the 'requires' entries in Module Info files + addStep(SortModuleInfoRequiresStep.create()) + // enable toggle comment support + toggleOffOn() + // don't need to set target, it is inferred from java + // apply a specific flavor of google-java-format + palantirJavaFormat() + // make sure every file has the following copyright header. + // optionally, Spotless can set copyright years by digging + // through git history (see "license" section below). + // The delimiter override below is required to support some + // of our test classes which are in the default package. + licenseHeader( + """ + /* + * Copyright (C) ${'$'}YEAR Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */${"\n\n"} + """ + .trimIndent(), + "(package|import|module)" + ) + .updateYearWithLatest(true) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless-kotlin.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless-kotlin.gradle.kts new file mode 100644 index 0000000..16ef258 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless-kotlin.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.diffplug.spotless") } + +spotless { + kotlinGradle { + ktfmt().kotlinlangStyle() + + licenseHeader( + """ + /* + * Copyright (C) ${'$'}YEAR Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */${"\n\n"} + """ + .trimIndent(), + "(import|plugins|pluginManagement|dependencyResolutionManagement|repositories|tasks|allprojects|subprojects|buildCache|version)" + ) + .updateYearWithLatest(true) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless-markdown.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless-markdown.gradle.kts new file mode 100644 index 0000000..9537e30 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless-markdown.gradle.kts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.diffplug.spotless") } + +spotless { + flexmark { + target("**/*.md") + targetExclude("platform-sdk/sdk/**", "node_modules/**") + flexmark() + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless-misc.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless-misc.gradle.kts new file mode 100644 index 0000000..b9d7244 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless-misc.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.diffplug.spotless") } + +spotless { + format("misc") { + // define the files to apply `misc` to + target(".gitignore") + + // define the steps to apply to those files + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless-yaml.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless-yaml.gradle.kts new file mode 100644 index 0000000..c1d0367 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless-yaml.gradle.kts @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.diffplug.spotless") } + +spotless { + format("actionYaml") { + target(".github/workflows/*.yaml") + /* + * Prettier requires NodeJS and NPM installed; however, the NodeJS Gradle plugin and Spotless do not yet + * integrate with each other. Currently there is an open issue report against spotless. + * + * *** Please see for more information: https://github.com/diffplug/spotless/issues/728 *** + * + * The workaround provided in the above issue does not work in Gradle 7.5+ and therefore is not a viable solution. + */ + // prettier() + + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + + licenseHeader( + """ + ## + # Copyright (C) ${'$'}YEAR Hedera Hashgraph, LLC + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + ##${"\n\n"} + """ + .trimIndent(), + "(name)" + ) + .updateYearWithLatest(true) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.check.spotless.gradle.kts b/src/main/kotlin/org.hiero.gradle.check.spotless.gradle.kts new file mode 100644 index 0000000..9ae0e6d --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.check.spotless.gradle.kts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("org.hiero.gradle.base.lifecycle") + id("com.diffplug.spotless") +} + +spotless { + // Disable the automatic application of Spotless to all source sets when the check task is run. + isEnforceCheck = false + + // limit format enforcement to just the files changed by this feature branch + @Suppress("UnstableApiUsage") + ratchetFrom( + "origin/" + + providers + .fileContents( + isolated.rootProject.projectDirectory.file("gradle/development-branch.txt") + ) + .asText + .getOrElse("main") + ) +} + +tasks.withType().configureEach { + // When doing a 'qualityGate' run, make sure spotlessApply is done before doing compilation and + // other checks based on compiled code + mustRunAfter(tasks.spotlessApply) +} + +tasks.named("qualityCheck") { dependsOn(tasks.spotlessCheck) } + +tasks.named("qualityGate") { dependsOn(tasks.spotlessApply) } diff --git a/src/main/kotlin/org.hiero.gradle.feature.antlr.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.antlr.gradle.kts new file mode 100644 index 0000000..0365dd8 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.antlr.gradle.kts @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("antlr") +} + +configurations { + // Treat the ANTLR compiler as a separate tool that should not end up on the compile/runtime + // classpath of our runtime. + // https://github.com/gradle/gradle/issues/820 + api { setExtendsFrom(extendsFrom.filterNot { it == antlr.get() }) } + // Get ANTLR version from 'hedera-dependency-versions' + antlr { extendsFrom(configurations["internal"]) } +} + +dependencies { antlr("org.antlr:antlr4") } + +// See: https://github.com/gradle/gradle/issues/25885 +tasks.named("sourcesJar") { dependsOn(tasks.generateGrammarSource) } + +tasks.withType().configureEach { + dependsOn(tasks.withType()) +} + +tasks.withType().configureEach { + dependsOn(tasks.withType()) +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.benchmark.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.benchmark.gradle.kts new file mode 100644 index 0000000..0ce4d6a --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.benchmark.gradle.kts @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import me.champeau.jmh.JMHTask + +plugins { id("me.champeau.jmh") } + +jmh { + jmhVersion = "1.37" + includeTests = false + // Filter JMH tests from command line via -PjmhTests=... + val commandLineIncludes = providers.gradleProperty("jmhTests") + if (commandLineIncludes.isPresent) { + includes.add(commandLineIncludes.get()) + } +} + +dependencies { + // Required for the JMH IDEA plugin: + // https://plugins.jetbrains.com/plugin/7529-jmh-java-microbenchmark-harness + jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:${jmh.jmhVersion.get()}") +} + +tasks.jmh { outputs.upToDateWhen { false } } + +tasks.withType().configureEach { + group = "jmh" + jarArchive = tasks.jmhJar.flatMap { it.archiveFile } + jvm = javaToolchains.launcherFor(java.toolchain).map { it.executablePath }.get().asFile.path +} + +tasks.jmhJar { manifest { attributes(mapOf("Multi-Release" to true)) } } + +configurations { + // Disable module Jar patching for the JMH runtime classpath. + // The way the JMH plugin interacts with this in the 'jmhJar' task triggers this Gradle issue: + // https://github.com/gradle/gradle/issues/27372 + // And since 'jmhJar' builds a fat jar, module information is not needed here anyway. + val javaModule = Attribute.of("javaModule", Boolean::class.javaObjectType) + jmhRuntimeClasspath { attributes { attribute(javaModule, false) } } + jmhCompileClasspath { attributes { attribute(javaModule, false) } } + jmhAnnotationProcessor { attributes { attribute(javaModule, false) } } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.build-cache.settings.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.build-cache.settings.gradle.kts new file mode 100644 index 0000000..d6cbd56 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.build-cache.settings.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +buildCache { + remote { + url = uri("https://cache.gradle.hedera.svcs.eng.swirldslabs.io/cache/") + + isUseExpectContinue = true + isEnabled = !gradle.startParameter.isOffline + + val isCiServer = providers.environmentVariable("CI").getOrElse("false").toBoolean() + val gradleCacheUsername = providers.environmentVariable("GRADLE_CACHE_USERNAME") + val gradleCachePassword = providers.environmentVariable("GRADLE_CACHE_PASSWORD") + if (isCiServer && gradleCacheUsername.isPresent && gradleCachePassword.isPresent) { + isPush = true + credentials { + username = gradleCacheUsername.get() + password = gradleCachePassword.get() + } + } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.git-properties-file.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.git-properties-file.gradle.kts new file mode 100644 index 0000000..c8f170d --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.git-properties-file.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +tasks.register("writeGitProperties") { + property("git.build.version", project.version) + @Suppress("UnstableApiUsage") + property( + "git.commit.id", + providers + .exec { commandLine("git", "rev-parse", "HEAD") } + .standardOutput + .asText + .map { it.trim() } + ) + @Suppress("UnstableApiUsage") + property( + "git.commit.id.abbrev", + providers + .exec { commandLine("git", "rev-parse", "HEAD") } + .standardOutput + .asText + .map { it.trim().substring(0, 7) } + ) + + destinationFile = layout.buildDirectory.file("generated/git/git.properties") +} + +tasks.processResources { from(tasks.named("writeGitProperties")) } + +// ignore the content of 'git.properties' when using a classpath as task input +normalization.runtimeClasspath { ignore("git.properties") } diff --git a/src/main/kotlin/org.hiero.gradle.feature.java-compile.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.java-compile.gradle.kts new file mode 100644 index 0000000..c84f128 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.java-compile.gradle.kts @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("org.gradlex.reproducible-builds") +} + +@Suppress("UnstableApiUsage") +val fullJavaVersion = + providers + .fileContents(isolated.rootProject.projectDirectory.file("gradle/jdk-version.txt")) + .asText + .orElse(provider { throw RuntimeException("gradle/jdk-version.txt file not found") }) + .get() + .trim() +val majorJavaVersion = JavaVersion.toVersion(fullJavaVersion) +val currentJavaVersion = providers.systemProperty("java.version").get() + +if (currentJavaVersion != fullJavaVersion) { + val message = + "Gradle runs with Java $currentJavaVersion. This project works best running with Java $fullJavaVersion. " + + "\n - From commandline: change JAVA_HOME and/or PATH to point at Java $fullJavaVersion installation." + + "\n - From IntelliJ: change 'Gradle JVM' in 'Gradle Settings' to point at Java $fullJavaVersion installation." + + logger.lifecycle("WARN: $message") +} + +java { + sourceCompatibility = majorJavaVersion + targetCompatibility = majorJavaVersion +} + +tasks.withType().configureEach { + // Track the full Java version as input (e.g. 17.0.3 vs. 17.0.9). + // By default, Gradle only tracks the major version as defined in the toolchain (e.g. 17). + // Since the full version is encoded in 'module-info.class' files, it should be tracked as + // it otherwise leads to wrong build cache hits. + inputs.property("fullJavaVersion", currentJavaVersion) + + options.javaModuleVersion = provider { project.version as String } + + options.isFork = true // run compiler in separate JVM process (independent of toolchain setup) + + doLast { + // Make sure consistent line ending are used in files generated by annotation processors by + // rewriting generated files. + // To fix this problem at the root, one of these issues needs to be addressed upstream: + // - https://github.com/google/auto/issues/1656 + // - https://github.com/gradle/gradle/issues/27385 + if (System.lineSeparator() != "\n") { + destinationDirectory + .get() + .asFileTree + .filter { it.extension != "class" } + .forEach { + val content = it.readText() + val normalizedContent = content.replace(System.lineSeparator(), "\n") + if (content != normalizedContent) { + it.writeText(normalizedContent) + } + } + } + } +} + +tasks.jar { manifest { attributes["Implementation-Version"] = project.version } } + +sourceSets.all { + // 'assemble' compiles all sources, including all test sources + tasks.assemble { dependsOn(tasks.named(classesTaskName)) } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.java-doc.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.java-doc.gradle.kts new file mode 100644 index 0000000..e959a1a --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.java-doc.gradle.kts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("org.gradlex.reproducible-builds") +} + +tasks.withType().configureEach { + options { + this as StandardJavadocDocletOptions + tags( + "apiNote:a:API Note:", + "implSpec:a:Implementation Requirements:", + "implNote:a:Implementation Note:" + ) + options.windowTitle = "Hedera Consensus Node" + options.memberLevel = JavadocMemberLevel.PACKAGE + addStringOption("Xdoclint:all,-missing", "-Xwerror") + } +} + +tasks.assemble { dependsOn(tasks.javadoc) } diff --git a/src/main/kotlin/org.hiero.gradle.feature.java-execute.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.java-execute.gradle.kts new file mode 100644 index 0000000..460525f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.java-execute.gradle.kts @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +tasks.withType().configureEach { + // Do not yet run things on the '--module-path' + modularity.inferModulePath = false + if (name.endsWith("main()")) { + notCompatibleWithConfigurationCache("JavaExec created by IntelliJ") + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.protobuf.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.protobuf.gradle.kts new file mode 100644 index 0000000..467460f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.protobuf.gradle.kts @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("com.google.protobuf") +} + +// Configure Protobuf Plugin to download protoc executable rather than using local installed version +protobuf { + protoc { artifact = "com.google.protobuf:protoc" } + // Add GRPC plugin as we need to generate GRPC services + plugins { register("grpc") { artifact = "io.grpc:protoc-gen-grpc-java" } } + generateProtoTasks { + all().configureEach { plugins.register("grpc") { option("@generated=omit") } } + } +} + +configurations.configureEach { + if (name.startsWith("protobufToolsLocator") || name.endsWith("ProtoPath")) { + attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API)) } + exclude(group = project.group.toString(), module = project.name) + withDependencies { + isTransitive = true + extendsFrom(configurations["internal"]) + } + } +} + +tasks.javadoc { + options { + this as StandardJavadocDocletOptions + // There are violations in the generated protobuf code + addStringOption("Xdoclint:-reference,-html", "-quiet") + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.publish-artifactregistry.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.publish-artifactregistry.gradle.kts new file mode 100644 index 0000000..9066b1e --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.publish-artifactregistry.gradle.kts @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("maven-publish") } + +if (gradle.startParameter.taskNames.any { it.startsWith("release") }) { + // We apply the 'artifactregistry' plugin conditionally, as it does not support configuration + // cache. + // https://github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/85 + apply(plugin = "com.google.cloud.artifactregistry.gradle-plugin") +} + +publishing.repositories { + maven("artifactregistry://us-maven.pkg.dev/swirlds-registry/maven-prerelease-channel") { + name = "prereleaseChannel" + } + maven("artifactregistry://us-maven.pkg.dev/swirlds-registry/maven-develop-snapshots") { + name = "developSnapshot" + } + maven("artifactregistry://us-maven.pkg.dev/swirlds-registry/maven-develop-daily-snapshots") { + name = "developDailySnapshot" + } + maven("artifactregistry://us-maven.pkg.dev/swirlds-registry/maven-develop-commits") { + name = "developCommit" + } + maven("artifactregistry://us-maven.pkg.dev/swirlds-registry/maven-adhoc-commits") { + name = "adhocCommit" + } +} + +// Register one 'release*' task for each publishing repository +publishing.repositories.all { + val ucName = name.replaceFirstChar { it.titlecase() } + tasks.register("release$ucName") { + group = "release" + dependsOn(tasks.named("publishMavenPublicationTo${ucName}Repository")) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.publish-gradle-plugin-portal.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.publish-gradle-plugin-portal.gradle.kts new file mode 100644 index 0000000..429fe06 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.publish-gradle-plugin-portal.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("com.gradle.plugin-publish") +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.gradle.kts new file mode 100644 index 0000000..fd3e295 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java") + id("maven-publish") + id("org.hiero.gradle.base.lifecycle") +} + +tasks.withType().configureEach { + // Publishing tasks are only enabled if we publish to the matching group. + // Otherwise, Nexus configuration and credentials do not fit. + val publishingPackageGroup = providers.gradleProperty("publishingPackageGroup").orNull + enabled = publishingPackageGroup == project.group +} + +publishing.publications.create("maven") { from(components["java"]) } + +tasks.named("releaseMavenCentral") { dependsOn(tasks.named("publishToSonatype")) } diff --git a/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.root.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.root.gradle.kts new file mode 100644 index 0000000..1501b72 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-central.root.gradle.kts @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("org.hiero.gradle.base.lifecycle") + id("io.github.gradle-nexus.publish-plugin") +} + +nexusPublishing { + val s01SonatypeHost = providers.gradleProperty("s01SonatypeHost").getOrElse("false").toBoolean() + val versionTxt = layout.projectDirectory.file("version.txt") + + packageGroup = providers.gradleProperty("publishingPackageGroup").getOrElse("") + useStaging = + providers.fileContents(versionTxt).asText.map { it.contains("-SNAPSHOT") }.getOrElse(false) + + repositories { + sonatype { + username = System.getenv("NEXUS_USERNAME") + password = System.getenv("NEXUS_PASSWORD") + if (s01SonatypeHost) { + nexusUrl = uri("https://s01.oss.sonatype.org/service/local/") + snapshotRepositoryUrl = + uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } + } + } +} + +tasks.named("closeSonatypeStagingRepository") { + // The publishing of all components to Maven Central is automatically done before close (which + // is done before release). + dependsOn(subprojects.map { ":${it.name}:releaseMavenCentral" }) +} + +tasks.named("releaseMavenCentral") { dependsOn(tasks.closeAndReleaseStagingRepository) } + +tasks.register("releaseMavenCentralSnapshot") { + group = "release" + dependsOn(subprojects.map { ":${it.name}:releaseMavenCentral" }) +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.publish-maven-repository.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-repository.gradle.kts new file mode 100644 index 0000000..9a80935 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.publish-maven-repository.gradle.kts @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Properties + +plugins { + id("java") + id("maven-publish") + id("signing") +} + +java { + withJavadocJar() + withSourcesJar() +} + +val publishSigningEnabled = + providers.gradleProperty("publishSigningEnabled").getOrElse("false").toBoolean() + +if (publishSigningEnabled) { + signing { + sign(publishing.publications) + useGpgCmd() + useInMemoryPgpKeys( + providers.environmentVariable("SIGNING_KEY").get(), + providers.environmentVariable("SIGNING_PASSPHRASE").get() + ) + } +} + +publishing.publications.withType().configureEach { + versionMapping { + // Everything published takes the versions from the resolution result. + // These are the versions we define in 'hedera-dependency-versions' + // and use consistently in all modules. + allVariants { fromResolutionResult() } + } + + suppressAllPomMetadataWarnings() + + pom { + val devGroups = Properties() + val developerProperties = layout.projectDirectory.file("../developers.properties") + devGroups.load( + providers + .fileContents(developerProperties) + .asText + .orElse( + provider { + throw RuntimeException("${developerProperties.asFile} does not exist") + } + ) + .get() + .reader() + ) + + url = "https://hiero.org/" + inceptionYear = "2024" + + description = + providers + .fileContents(layout.projectDirectory.file("../description.txt")) + .asText + .orElse(provider { project.description }) + .map { it.replace("\n", " ").trim() } + .orNull + + organization { + name = "Hiero - a Linux Foundation Decentralized Trust project" + url = "https://hiero.org/" + } + + @Suppress("UnstableApiUsage") val repoName = isolated.rootProject.name + + issueManagement { + system = "GitHub" + url = "https://github.com/hiero-ledger/$repoName/issues" + } + + licenses { + license { + name = "Apache License, Version 2.0" + url = "https://raw.githubusercontent.com/hiero-ledger/$repoName/main/LICENSE" + } + } + + scm { + connection = "scm:git:git://github.com/hiero-ledger/$repoName.git" + developerConnection = "scm:git:ssh://github.com:hiero-ledger/$repoName.git" + url = "https://github.com/hiero-ledger/$repoName" + } + + developers { + devGroups.forEach { mail, team -> + developer { + name = team as String + email = mail as String + organization = "Hiero - a Linux Foundation Decentralized Trust project" + organizationUrl = "https://hiero.org/" + } + } + } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.repositories.settings.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.repositories.settings.gradle.kts new file mode 100644 index 0000000..bc545a7 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.repositories.settings.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +dependencyResolutionManagement { + @Suppress("UnstableApiUsage") + repositories { + maven("https://hyperledger.jfrog.io/artifactory/besu-maven") { + content { includeGroupByRegex("org\\.hyperledger\\..*") } + } + maven("https://artifacts.consensys.net/public/maven/maven/") { + content { includeGroupByRegex("tech\\.pegasys(\\..*)?") } + } + maven("https://jitpack.io") { + content { includeModule("io.github.cdimascio", "java-dotenv") } + } + mavenCentral() + maven("https://oss.sonatype.org/content/repositories/snapshots") + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.rust.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.rust.gradle.kts new file mode 100644 index 0000000..275fe2f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.rust.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.hiero.gradle.extensions.CargoExtension +import org.hiero.gradle.extensions.CargoToolchain.* +import org.hiero.gradle.services.TaskLockService +import org.hiero.gradle.tasks.CargoBuildTask + +plugins { id("java") } + +val cargo = project.extensions.create("cargo") + +cargo.targets(aarch64Darwin, aarch64Linux, x86Darwin, x86Linux, x86Windows) + +// Cargo might do installation work, do not run in parallel: +tasks.withType().configureEach { + usesService( + gradle.sharedServices.registerIfAbsent("lock", TaskLockService::class) { + maxParallelUsages = 1 + } + ) +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-fixtures.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-fixtures.gradle.kts new file mode 100644 index 0000000..efaaf9f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-fixtures.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.gradle.api.component.AdhocComponentWithVariants + +plugins { id("java-test-fixtures") } + +// Disable publishing of test fixture if 'java-test-fixtures' plugin is used +// https://docs.gradle.org/current/userguide/java_testing.html#ex-disable-publishing-of-test-fixtures-variants +(components["java"] as AdhocComponentWithVariants).apply { + withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() } + withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-hammer.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-hammer.gradle.kts new file mode 100644 index 0000000..a7ab230 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-hammer.gradle.kts @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.hiero.gradle.services.TaskLockService + +plugins { id("java") } + +// Test functionally correct behavior under stress/loads with many repeated iterations. +@Suppress("UnstableApiUsage") +testing.suites { + register("hammer") { + testType = "hammer" + targets.all { + testTask { + maxHeapSize = "8g" + usesService( + gradle.sharedServices.registerIfAbsent("lock", TaskLockService::class) { + maxParallelUsages = 1 + } + ) + } + } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-integration.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-integration.gradle.kts new file mode 100644 index 0000000..270fc1e --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-integration.gradle.kts @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +// Tests that could be in the default "test" set but take more than 100ms to execute. +@Suppress("UnstableApiUsage") +testing.suites { + register("testIntegration") { testType = TestSuiteType.INTEGRATION_TEST } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-multios.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-multios.gradle.kts new file mode 100644 index 0000000..87fc9c5 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-multios.gradle.kts @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +tasks.test { + inputs.property("operatingSystemName", System.getProperty("os.name")) + inputs.property("operatingSystemArch", System.getProperty("os.arch")) +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-time-consuming.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-time-consuming.gradle.kts new file mode 100644 index 0000000..a9c56c7 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-time-consuming.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("java") } + +// Tests that could be in the default "test" set but take more than 100ms to execute. +@Suppress("UnstableApiUsage") +testing.suites { + register("timeConsuming") { + testType = "timing-consuming" + targets.all { testTask { maxHeapSize = "4g" } } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test-timing-sensitive.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test-timing-sensitive.gradle.kts new file mode 100644 index 0000000..1a0e4c0 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test-timing-sensitive.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.hiero.gradle.services.TaskLockService + +plugins { id("java") } + +// Tests that are resource sensitive (e.g. use Thread.sleep()) and thus need to run without anything +// in parallel. +@Suppress("UnstableApiUsage") +testing.suites { + register("timingSensitive") { + testType = "timing-sensitive" + targets.all { + testTask { + maxHeapSize = "4g" + usesService( + gradle.sharedServices.registerIfAbsent("lock", TaskLockService::class) { + maxParallelUsages = 1 + } + ) + } + } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.test.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.test.gradle.kts new file mode 100644 index 0000000..dbf7778 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.test.gradle.kts @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.hiero.gradle.services.TaskLockService + +plugins { id("java") } + +@Suppress("UnstableApiUsage") +testing.suites { + named("test") { + useJUnitJupiter() + targets.all { + testTask { + maxHeapSize = "4g" + // Some tests overlap due to using the same temp folders within one project + // maxParallelForks = 4 <- set this, once tests can run in parallel + + // Enable dynamic agent loading for tests - eg: Mockito, ByteBuddy + jvmArgs("-XX:+EnableDynamicAgentLoading") + } + } + } + // remove automatically added compile time dependencies, as we define them explicitly + withType { + configurations.getByName(sources.implementationConfigurationName) { + withDependencies { + removeIf { it.group == "org.junit.jupiter" && it.name == "junit-jupiter" } + } + } + dependencies { runtimeOnly("org.junit.jupiter:junit-jupiter-engine") } + } +} + +// If user gave the argument '-PactiveProcessorCount', then do: +// - run all test tasks in sequence +// - give the -XX:ActiveProcessorCount argument to the test JVMs +val activeProcessorCount = providers.gradleProperty("activeProcessorCount") + +if (activeProcessorCount.isPresent) { + tasks.withType().configureEach { + usesService( + gradle.sharedServices.registerIfAbsent("lock", TaskLockService::class) { + maxParallelUsages = 1 + } + ) + jvmArgs("-XX:ActiveProcessorCount=${activeProcessorCount.get()}") + } +} diff --git a/src/main/kotlin/org.hiero.gradle.feature.versioning.gradle.kts b/src/main/kotlin/org.hiero.gradle.feature.versioning.gradle.kts new file mode 100644 index 0000000..ba941a0 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.feature.versioning.gradle.kts @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import net.swiftzer.semver.SemVer + +val versionTxt = layout.projectDirectory.file("version.txt") + +tasks.register("githubVersionSummary") { + group = "versioning" + + inputs.property("productName", project.name) + inputs.property("version", providers.fileContents(versionTxt).asText.map { it.trim() }) + + if (!providers.environmentVariable("GITHUB_STEP_SUMMARY").isPresent) { + // Do not throw an exception if running the `gradlew tasks` task + if (project.gradle.startParameter.taskNames.contains("githubVersionSummary")) { + throw IllegalArgumentException( + "This task may only be run in a Github Actions CI environment! " + + "Unable to locate the GITHUB_STEP_SUMMARY environment variable." + ) + } + } + outputs.file(providers.environmentVariable("GITHUB_STEP_SUMMARY")) + + doLast { + val version = inputs.properties["version"] as String + val productName = inputs.properties["productName"] as String + + outputs.files.singleFile.writeText( + """ + ### Deployed Version Information + + | Artifact Name | Version Number | + | --- | --- | + | $productName | $version | + """ + .trimIndent() + ) + } +} + +tasks.register("showVersion") { + group = "versioning" + + inputs.property("version", providers.fileContents(versionTxt).asText.map { it.trim() }) + + doLast { println(inputs.properties["version"]) } +} + +tasks.register("versionAsPrefixedCommit") { + group = "versioning" + + @Suppress("UnstableApiUsage") + inputs.property( + "commit", + providers + .exec { commandLine("git", "rev-parse", "HEAD") } + .standardOutput + .asText + .map { it.trim().substring(0, 7) } + ) + inputs.property("commitPrefix", providers.gradleProperty("commitPrefix").orElse("adhoc")) + inputs.property("version", providers.fileContents(versionTxt).asText.map { it.trim() }) + outputs.file(versionTxt) + + doLast { + val newPrerel = + inputs.properties["commitPrefix"].toString() + + ".x" + + inputs.properties["commit"].toString().take(8) + val currVer = SemVer.parse(inputs.properties["version"] as String) + val newVer = SemVer(currVer.major, currVer.minor, currVer.patch, newPrerel) + outputs.files.singleFile.writeText(newVer.toString()) + } +} + +tasks.register("versionAsSnapshot") { + group = "versioning" + + inputs.property("version", providers.fileContents(versionTxt).asText.map { it.trim() }) + outputs.file(versionTxt) + + doLast { + val currVer = SemVer.parse(inputs.properties["version"] as String) + val newVer = SemVer(currVer.major, currVer.minor, currVer.patch, "SNAPSHOT") + + outputs.files.singleFile.writeText(newVer.toString()) + } +} + +tasks.register("versionAsSpecified") { + group = "versioning" + + inputs.property("newVersion", providers.gradleProperty("newVersion")).optional(true) + + doLast { + val newVer = + inputs.properties["newVersion"] as String? + ?: throw IllegalArgumentException( + "No newVersion property provided! " + + "Please add the parameter -PnewVersion= when running this task." + ) + outputs.files.singleFile.writeText(SemVer.parse(newVer).toString()) + } +} diff --git a/src/main/kotlin/org.hiero.gradle.module.application.gradle.kts b/src/main/kotlin/org.hiero.gradle.module.application.gradle.kts new file mode 100644 index 0000000..f7c7a67 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.module.application.gradle.kts @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("application") + id("jacoco") + id("org.hiero.gradle.base.jpms-modules") + id("org.hiero.gradle.base.lifecycle") + id("org.hiero.gradle.base.version") + id("org.hiero.gradle.check.dependencies") + id("org.hiero.gradle.check.javac-lint") + id("org.hiero.gradle.check.spotless") + id("org.hiero.gradle.check.spotless-java") + id("org.hiero.gradle.check.spotless-kotlin") + id("org.hiero.gradle.feature.git-properties-file") + id("org.hiero.gradle.feature.java-compile") + id("org.hiero.gradle.feature.java-doc") + id("org.hiero.gradle.feature.java-execute") + id("org.hiero.gradle.feature.test") + id("org.hiero.gradle.report.test-logger") +} + +// Make the Jar itself executable by setting the 'Main-Class' manifest attribute. +tasks.jar { manifest { attributes("Main-Class" to application.mainClass) } } + +// The 'application' plugin activates the following tasks as part of 'assemble'. +// As we do not use these results right now, disable them: +tasks.startScripts { enabled = false } + +tasks.distTar { enabled = false } + +tasks.distZip { enabled = false } diff --git a/src/main/kotlin/org.hiero.gradle.module.gradle-plugin.gradle.kts b/src/main/kotlin/org.hiero.gradle.module.gradle-plugin.gradle.kts new file mode 100644 index 0000000..d61758b --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.module.gradle-plugin.gradle.kts @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java-library") + id("org.hiero.gradle.feature.publish-maven-repository") + id("org.hiero.gradle.feature.publish-gradle-plugin-portal") + id("jacoco") + id("org.hiero.gradle.base.jpms-modules") + id("org.hiero.gradle.base.lifecycle") + id("org.hiero.gradle.base.version") + id("org.hiero.gradle.check.dependencies") + id("org.hiero.gradle.check.javac-lint") + id("org.hiero.gradle.check.spotless") + id("org.hiero.gradle.check.spotless-java") + id("org.hiero.gradle.check.spotless-kotlin") + id("org.hiero.gradle.feature.git-properties-file") + id("org.hiero.gradle.feature.java-compile") + id("org.hiero.gradle.feature.java-doc") + id("org.hiero.gradle.feature.java-execute") + id("org.hiero.gradle.feature.test") + id("org.hiero.gradle.report.test-logger") +} + +extraJavaModuleInfo { + failOnMissingModuleInfo = false + failOnAutomaticModules = false +} diff --git a/src/main/kotlin/org.hiero.gradle.module.library.gradle.kts b/src/main/kotlin/org.hiero.gradle.module.library.gradle.kts new file mode 100644 index 0000000..7ac39b0 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.module.library.gradle.kts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java-library") + id("org.hiero.gradle.feature.publish-maven-repository") + id("org.hiero.gradle.feature.publish-maven-central") + id("jacoco") + id("org.hiero.gradle.base.jpms-modules") + id("org.hiero.gradle.base.lifecycle") + id("org.hiero.gradle.base.version") + id("org.hiero.gradle.check.dependencies") + id("org.hiero.gradle.check.javac-lint") + id("org.hiero.gradle.check.spotless") + id("org.hiero.gradle.check.spotless-java") + id("org.hiero.gradle.check.spotless-kotlin") + id("org.hiero.gradle.feature.git-properties-file") + id("org.hiero.gradle.feature.java-compile") + id("org.hiero.gradle.feature.java-doc") + id("org.hiero.gradle.feature.java-execute") + id("org.hiero.gradle.feature.test") + id("org.hiero.gradle.report.test-logger") +} diff --git a/src/main/kotlin/org.hiero.gradle.report.code-coverage.gradle.kts b/src/main/kotlin/org.hiero.gradle.report.code-coverage.gradle.kts new file mode 100644 index 0000000..59c5e33 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.report.code-coverage.gradle.kts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesScopeCheck + +plugins { + id("java") + id("jacoco-report-aggregation") + id("org.hiero.gradle.base.jpms-modules") +} + +tasks.withType { enabled = false } + +// Make aggregation "classpath" use the platform for versions (gradle/versions) +configurations.aggregateCodeCoverageReportResults { extendsFrom(configurations["internal"]) } diff --git a/src/main/kotlin/org.hiero.gradle.report.develocity.settings.gradle.kts b/src/main/kotlin/org.hiero.gradle.report.develocity.settings.gradle.kts new file mode 100644 index 0000000..3c1149f --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.report.develocity.settings.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { id("com.gradle.develocity") } + +develocity { + buildScan { + termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" + termsOfUseAgree = "yes" + // Enable Gradle Build Scan only with explicit '--scan' + publishing.onlyIf { false } + } +} diff --git a/src/main/kotlin/org.hiero.gradle.report.test-logger.gradle.kts b/src/main/kotlin/org.hiero.gradle.report.test-logger.gradle.kts new file mode 100644 index 0000000..16e7686 --- /dev/null +++ b/src/main/kotlin/org.hiero.gradle.report.test-logger.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.adarshr.gradle.testlogger.theme.ThemeType + +plugins { id("com.adarshr.test-logger") } + +testlogger { + theme = ThemeType.MOCHA_PARALLEL + slowThreshold = 10000 + showPassed = false + showSkipped = false + showStandardStreams = true + showPassedStandardStreams = false + showSkippedStandardStreams = false + showFailedStandardStreams = true +} diff --git a/src/main/kotlin/org/hiero/gradle/extensions/CargoExtension.kt b/src/main/kotlin/org/hiero/gradle/extensions/CargoExtension.kt new file mode 100644 index 0000000..4c8610f --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/extensions/CargoExtension.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.extensions + +import javax.inject.Inject +import org.gradle.api.Project +import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.Property +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskContainer +import org.gradle.kotlin.dsl.register +import org.hiero.gradle.tasks.CargoBuildTask + +@Suppress("LeakingThis") +abstract class CargoExtension { + abstract val cargoBin: Property + abstract val libname: Property + abstract val release: Property + + @get:Inject protected abstract val project: Project + + @get:Inject protected abstract val layout: ProjectLayout + + @get:Inject protected abstract val tasks: TaskContainer + + @get:Inject protected abstract val sourceSets: SourceSetContainer + + init { + cargoBin.convention(System.getProperty("user.home") + "/.cargo/bin") + libname.convention(project.name) + release.convention(true) + + // Lifecycle task to only do all carg build tasks (mainly for testing) + project.tasks.register("cargoBuild") { + group = "rust" + description = "Build library (all targets)" + } + } + + fun targets(vararg targets: CargoToolchain) { + targets.forEach { target -> + val targetBuildTask = + tasks.register( + "cargoBuild${target.name.replaceFirstChar(Char::titlecase)}" + ) { + group = "rust" + description = "Build library ($target)" + toolchain.convention(target) + sourcesDirectory.convention(layout.projectDirectory.dir("src/main/rust")) + destinationDirectory.convention( + layout.buildDirectory.dir("rustJniLibs/${target.platform}") + ) + + this.cargoToml.convention(layout.projectDirectory.file("Cargo.toml")) + this.libname.convention(this@CargoExtension.libname) + this.release.convention(this@CargoExtension.release) + this.cargoBin.convention(this@CargoExtension.cargoBin) + @Suppress("UnstableApiUsage") + this.xwinFolder.convention( + project.isolated.rootProject.projectDirectory + .dir(".gradle/xwin") + .asFile + .absolutePath + ) + } + + tasks.named("cargoBuild") { dependsOn(targetBuildTask) } + sourceSets.getByName("main").resources.srcDir(targetBuildTask) + } + } +} diff --git a/src/main/kotlin/org/hiero/gradle/extensions/CargoToolchain.kt b/src/main/kotlin/org/hiero/gradle/extensions/CargoToolchain.kt new file mode 100644 index 0000000..ead83ea --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/extensions/CargoToolchain.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.extensions + +enum class CargoToolchain(val platform: String, val target: String, val folder: String) { + aarch64Darwin("darwin-aarch64", "aarch64-apple-darwin", "software/darwin/arm64"), + aarch64Linux("linux-aarch64", "aarch64-unknown-linux-gnu", "software/linux/arm64"), + x86Darwin("darwin-x86-64", "x86_64-apple-darwin", "software/darwin/amd64"), + x86Linux("linux-x86-64", "x86_64-unknown-linux-gnu", "software/linux/amd64"), + x86Windows("win32-x86-64-msvc", "x86_64-pc-windows-msvc", "software/windows/amd64") +} diff --git a/src/main/kotlin/org/hiero/gradle/services/TaskLockService.kt b/src/main/kotlin/org/hiero/gradle/services/TaskLockService.kt new file mode 100644 index 0000000..f63701e --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/services/TaskLockService.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.services + +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters + +abstract class TaskLockService : BuildService diff --git a/src/main/kotlin/org/hiero/gradle/spotless/RepairDashedCommentsFormatterStep.kt b/src/main/kotlin/org/hiero/gradle/spotless/RepairDashedCommentsFormatterStep.kt new file mode 100644 index 0000000..b0f16ce --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/spotless/RepairDashedCommentsFormatterStep.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.spotless + +import com.diffplug.spotless.FormatterFunc +import com.diffplug.spotless.FormatterStep + +/** + * Adds self-correcting behavior as spotless step which properly removes the comments which causes + * the google-java-formatter plugin to rupture (eg: \/\*-). + */ +class RepairDashedCommentsFormatterStep { + companion object { + private const val NAME = "RepairDashedComments" + private const val OPENING_COMMENT_REGEX = "/\\*-+" + private const val CLOSING_COMMENT_REGEX = "-+\\*/" + + fun create(): FormatterStep { + val openingCommentRegex = Regex(OPENING_COMMENT_REGEX, setOf(RegexOption.IGNORE_CASE)) + val closingCommentRegex = Regex(CLOSING_COMMENT_REGEX, setOf(RegexOption.IGNORE_CASE)) + return FormatterStep.create( + NAME, + State(openingCommentRegex, closingCommentRegex), + State::toFormatter + ) + } + } + + private class State(val openingCommentRegex: Regex, val closingCommentRegex: Regex) : + java.io.Serializable { + + fun toFormatter(): FormatterFunc { + return FormatterFunc { unixStr -> + val lines = unixStr.split('\n') + val result = ArrayList(lines.size) + var inLicenseBlock = false + + lines.forEach { s -> + if (!inLicenseBlock && s.trim().equals("/*-")) { + inLicenseBlock = true + } else if (inLicenseBlock && s.trim().equals("*/")) { + inLicenseBlock = false + } + + if (inLicenseBlock) { + result.add(s) + } else { + result.add( + s.replace(openingCommentRegex, "/*").replace(closingCommentRegex, "*/") + ) + } + } + + val finalStr = result.joinToString("\n") + finalStr + } + } + } +} diff --git a/src/main/kotlin/org/hiero/gradle/spotless/SortModuleInfoRequiresStep.kt b/src/main/kotlin/org/hiero/gradle/spotless/SortModuleInfoRequiresStep.kt new file mode 100644 index 0000000..650f0e7 --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/spotless/SortModuleInfoRequiresStep.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.spotless + +import com.diffplug.spotless.FormatterFunc +import com.diffplug.spotless.FormatterStep + +class SortModuleInfoRequiresStep { + companion object { + private const val NAME = "SortModuleInfoRequires" + private val OWN_PACKAGES = listOf("com.swirlds.", "com.hedera", "org.hiero") + + fun create(): FormatterStep { + return FormatterStep.create(NAME, State(), State::toFormatter) + } + } + + private class State : java.io.Serializable { + + fun toFormatter(): FormatterFunc { + return FormatterFunc { unixStr -> + val lines = unixStr.split('\n') + val blockStartIndex = lines.indexOfFirst { it.trim().startsWith("requires") } + val blockEndIndex = lines.indexOfLast { it.trim().startsWith("requires") } + + if (blockStartIndex == -1) { + unixStr // not a module-info.java or no 'requires' + } else { + val nonRequiresLines = mutableListOf() + + val requiresTransitive = mutableListOf() + val requires = mutableListOf() + val requiresStaticTransitive = mutableListOf() + val requiresStatic = mutableListOf() + + lines.subList(blockStartIndex, blockEndIndex + 1).forEach { line -> + when { + line.trim().startsWith("requires static transitive") -> + requiresStaticTransitive.add(line) + line.trim().startsWith("requires static") -> requiresStatic.add(line) + line.trim().startsWith("requires transitive") -> + requiresTransitive.add(line) + line.trim().startsWith("requires") -> requires.add(line) + line.isNotBlank() && !line.trim().startsWith("requires") -> + nonRequiresLines.add(line) + } + } + + val comparator = + Comparator { a, b -> + val nameA = a.split(" ").first { it.endsWith(";") } + val nameB = b.split(" ").first { it.endsWith(";") } + if ( + OWN_PACKAGES.any { nameA.startsWith(it) } && + OWN_PACKAGES.none { nameB.startsWith(it) } + ) { + -1 + } else if ( + OWN_PACKAGES.none { nameA.startsWith(it) } && + OWN_PACKAGES.any { nameB.startsWith(it) } + ) { + 1 + } else { + nameA.compareTo(nameB) + } + } + + requiresTransitive.sortWith(comparator) + requires.sortWith(comparator) + requiresStaticTransitive.sortWith(comparator) + requiresStatic.sortWith(comparator) + + val blockStart = lines.subList(0, blockStartIndex) + val blockEnd = lines.subList(blockEndIndex + 1, lines.size) + + (blockStart + + nonRequiresLines + + requiresTransitive + + requires + + requiresStaticTransitive + + requiresStatic + + blockEnd) + .joinToString("\n") + } + } + } + } +} diff --git a/src/main/kotlin/org/hiero/gradle/tasks/CargoBuildTask.kt b/src/main/kotlin/org/hiero/gradle/tasks/CargoBuildTask.kt new file mode 100644 index 0000000..5c850b2 --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/tasks/CargoBuildTask.kt @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.tasks + +import java.io.File +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.internal.file.FileOperations +import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import org.hiero.gradle.extensions.CargoToolchain + +@CacheableTask +abstract class CargoBuildTask : DefaultTask() { + @get:Input abstract val libname: Property + + @get:Input abstract val release: Property + + @get:Input abstract val toolchain: Property + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val cargoToml: RegularFileProperty + + @get:InputDirectory + @get:PathSensitive(PathSensitivity.NAME_ONLY) + abstract val sourcesDirectory: DirectoryProperty + + @get:OutputDirectory abstract val destinationDirectory: DirectoryProperty + + @get:Internal abstract val cargoBin: Property + + @get:Internal abstract val xwinFolder: Property + + @get:Inject protected abstract val exec: ExecOperations + + @get:Inject protected abstract val files: FileOperations + + @TaskAction + fun build() { + val buildsForWindows = toolchain.get() == CargoToolchain.x86Windows + + installCargoCrossCompiler(buildsForWindows) + buildForTarget(buildsForWindows) + + val profile = if (release.get()) "release" else "debug" + val cargoOutputDir = + File(cargoToml.get().asFile.parent, "target/${toolchain.get().target}/${profile}") + + files.copy { + from(cargoOutputDir) + into(destinationDirectory.dir(toolchain.get().folder)) + + include("lib${libname.get()}.so") + include("lib${libname.get()}.dylib") + include("${libname.get()}.dll") + } + } + + private fun installCargoCrossCompiler(buildsForWindows: Boolean) { + exec.exec { + // Pin version to latest 0.6.6 RC to due to issue + // https://github.com/Jake-Shadle/xwin/issues/141 + // Version should be removed, once 0.6.6 or newer is officially available + val crossCompiler = if (buildsForWindows) "xwin@0.6.6-rc.2" else "cargo-zigbuild" + commandLine = listOf(cargoBin.get() + "/cargo", "install", "--locked", crossCompiler) + } + if (buildsForWindows && !File(xwinFolder.get()).exists()) { + exec.exec { + // https://github.com/Jake-Shadle/xwin/issues/141#issuecomment-2416864318 + environment("RAYON_NUM_THREADS", "1") + commandLine = + listOf( + cargoBin.get() + "/xwin", + "--accept-license", + "splat", + "--output", + xwinFolder.get() + ) + } + } + } + + private fun buildForTarget(buildsForWindows: Boolean) { + exec.exec { + val buildCommand = if (buildsForWindows) "build" else "zigbuild" + + workingDir = cargoToml.get().asFile.parentFile + + commandLine = + listOf( + cargoBin.get() + "/cargo", + buildCommand, + "--target=${toolchain.get().target}" + ) + + if (buildsForWindows) { + // See https://github.com/Jake-Shadle/xwin/blob/main/xwin.dockerfile + val xwin = xwinFolder.get() + val clFlags = + "-Wno-unused-command-line-argument -fuse-ld=lld-link /vctoolsdir $xwin/crt /winsdkdir $xwin/sdk" + environment("CC_x86_64_pc_windows_msvc", "clang-cl") + environment("CXX_x86_64_pc_windows_msvc", "clang-cl") + environment("AR_x86_64_pc_windows_msvc", "llvm-lib") + environment("WINEDEBUG", "-all") + environment("CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUNNER", "wine") + environment("CL_FLAGS", clFlags) + environment("CFLAGS_x86_64_pc_windows_msvc", clFlags) + environment("CXXFLAGS_x86_64_pc_windows_msvc", clFlags) + environment("CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER", "lld-link") + environment( + "RUSTFLAGS", + "-Lnative=$xwin/crt/lib/x86_64 -Lnative=$xwin/sdk/lib/um/x86_64 -Lnative=$xwin/sdk/lib/ucrt/x86_64" + ) + } + + if (release.get()) { + args("--release") + } + if (logger.isEnabled(LogLevel.INFO)) { + // For '--info' logging, turn on '--verbose' + args("--verbose") + } + } + } +} diff --git a/src/main/kotlin/org/hiero/gradle/tasks/GitClone.kt b/src/main/kotlin/org/hiero/gradle/tasks/GitClone.kt new file mode 100644 index 0000000..efc9ed0 --- /dev/null +++ b/src/main/kotlin/org/hiero/gradle/tasks/GitClone.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022-2024 Hiero a Series of LF Projects, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hiero.gradle.tasks + +import javax.inject.Inject +import org.gradle.StartParameter +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations + +@Suppress("LeakingThis") +abstract class GitClone : DefaultTask() { + + @get:Input abstract val url: Property + + @get:Input @get:Optional abstract val tag: Property + + @get:Input @get:Optional abstract val branch: Property + + @get:Input abstract val offline: Property + + @get:OutputDirectory abstract val localCloneDirectory: DirectoryProperty + + @get:Inject protected abstract val exec: ExecOperations + + @get:Inject protected abstract val startParameter: StartParameter + + @get:Inject protected abstract val layout: ProjectLayout + + init { + offline.set(startParameter.isOffline) + localCloneDirectory.set(layout.buildDirectory.dir("hedera-protobufs")) + // If a 'branch' is configured, the task is never up-to-date as it may change + outputs.upToDateWhen { !branch.isPresent } + } + + @TaskAction + fun cloneOrUpdate() { + if (!tag.isPresent && !branch.isPresent || tag.isPresent && branch.isPresent) { + throw RuntimeException("Define either 'tag' or 'branch'") + } + + val localClone = localCloneDirectory.get() + if (!offline.get()) { + exec.exec { + if (!localClone.dir(".git").asFile.exists()) { + workingDir = localClone.asFile.parentFile + commandLine("git", "clone", url.get(), "-q") + } else { + workingDir = localClone.asFile + commandLine("git", "fetch", "-q") + } + } + } + if (tag.isPresent) { + exec.exec { + workingDir = localClone.asFile + commandLine("git", "checkout", tag.get(), "-q") + } + exec.exec { + workingDir = localClone.asFile + commandLine("git", "reset", "--hard", tag.get(), "-q") + } + } else { + exec.exec { + workingDir = localClone.asFile + commandLine("git", "checkout", branch.get(), "-q") + } + exec.exec { + workingDir = localClone.asFile + commandLine("git", "reset", "--hard", "origin/${branch.get()}", "-q") + } + } + } +}